Keri sisuni

Põhiprintsiibid

Kapseldus

Kapseldus (encapsulation) tähendab klasside omadust peita oma sisemist olekut teiste klasside eest ning pakkuda neile välja suhtlemiseks mõeldud liides. See tähendab lihtsamas keeles seda, et klass on nagu masin, millel on oma sisemine toimimise pool ja teistele klassidele on kasutamiseks mõeldud avalikud meetodid ja omadused.

Vaatame näitena failide logimise klassi FileLogger. Pööra tähelepanu esiletõstetud ridadele.

public class FileLogger : ILogger, IDisposable
{
    private readonly FileStream _fileStream;
    private readonly StreamWriter _streamWriter;

    public FileLogger(string fileName)
    {
        _fileStream = new FileStream(fileName, FileMode.OpenOrCreate);
        _fileStream.Seek(0, SeekOrigin.End);

        _streamWriter = new StreamWriter(_fileStream);
    }

    public void Info(string message)
    {
        Write("INFO: " + message);
    }

    public void Error(string message)
    {
        Write("ERROR: " + message);
    }

    private void Write(string message)
    {
        _streamWriter.WriteLine(message);
    }

    public void Dispose()
    {
        _streamWriter.Flush();
        _streamWriter.Dispose();
        _fileStream.Dispose();
    }
}

Failisüsteemiga suhtlemise tehnilised detailid on "kapseldatud" FileLogger klassi sisse privaatsesse skoopi. Ükski teine klass "ei näe", et FileLogger kasutab FileStream ja StreamWriter klasse.

Mõtleme korra, mis saaks valesti minna kui me kapseldamise printsiipi rikume ja teeme FileLogger klassi sisemuse teistele klassidele nähtavaks. Kõik, mis FileLogger klassil on, on nüüd public.

public class FileLogger : ILogger, IDisposable
{
    public readonly FileStream _fileStream;
    public readonly StreamWriter _streamWriter;

    public FileLogger(string fileName)
    {
        _fileStream = new FileStream(fileName, FileMode.OpenOrCreate);
        _fileStream.Seek(0, SeekOrigin.End);

        _streamWriter = new StreamWriter(_fileStream);
    }

    public void Info(string message)
    {
        Write("INFO: " + message);
    }

    public void Error(string message)
    {
       Write("ERROR: " + message);
    }

    public void Write(string message)
    {
        _streamWriter.WriteLine(message);
    }

    public void Dispose()
    {
        _streamWriter.Flush();
        _streamWriter.Dispose();
        _fileStream.Dispose();
    }
}

Nüüd pääsevad teised klassid FileLoggeri kõikidele osadele juurde. Mis on klassi sisemine kood ja mis on mõeldud teistele kasutamiseks, pole enam arusaadav.

Mis juhtub praktikas

Teised arendajad võivad FileLoggerit kasutada valesti - selliselt nagu me ei eelda. Miks nad seda teevad? Sest me ise lubame - kõik on ju public. Abi poleks sellestki kui kirjutaksime kommentaarid juurde, sest kommentaarid on pahatihti vanad ja aegunud ning neid ei usu arendajad nii palju kui märksõnu public, private ja protected.

Kõige hullem juhtub ennast liigselt avanud klassidega aja jooksul. Programmi koodiga töötavad erinevad arendajad. Neist värskemad tulijad ei pruugi meie logimise klassist midagi teada ja võib-olla nad isegi ei vaata sellele klassile peale. Küll aga näevad nad, et klassil on olemas StreamWriter tüüpi omadus ja Write() meetod, mida saab ise otse kutsuda. Tasapisi tekib juurde ja juurde uut koodi, mis liiga avatud klassi valesti kasutab. See tähendab jällegi seda, et liiga avatud klassil on järjest rohkem seoseid teiste klassidega. Ühel hetkel saabub koht, kus olukorra parandamine pole enam tunni-paari, vaid paari-kolme tööpäeva jagu tegemist.

Abstraheerimine

Abstraheerimine (abstraction) on tihedalt seotud kapseldusega ning see tähendab seda, et klassid pakuvad suhtlemiseks välja arusaadava ja lihtsasti mõistetava liidese, mis ei sisalda liigseid detaile nende toimimise kohta.

Jätkame FileLogger klassi uurimisega. Kui kapselduse juures oli teemaks see, et klassi sisemised detailid ei paistaks teistele klassidele välja, siis abstraheerimine paneb paika klassi avaliku poole.

public class FileLogger : ILogger, IDisposable
{
    private readonly FileStream _fileStream;
    private readonly StreamWriter _streamWriter;

    public FileLogger(string fileName)
    {
        _fileStream = new FileStream(fileName, FileMode.OpenOrCreate);
        _fileStream.Seek(0, SeekOrigin.End);

        _streamWriter = new StreamWriter(_fileStream);
    }

    public void Info(string message)
    {
        Write("INFO: " + message);
    }

    public void Error(string message)
    {
       Write("ERROR: " + message);
    }

    private void Write(string message)
    {
        _streamWriter.WriteLine(message);
    }

    public void Dispose()
    {
        _streamWriter.Flush();
        _streamWriter.Dispose();
        _fileStream.Dispose();
    }
}

Klassi avalik pool on klassi suhtlusliides, mida näevad kõik teised klassid meie koodis. See peab olema nii minimaalne kui võimalik, sest mida väiksem on suhtlusliides kahe klassi vahel, seda vähem klassid teineteisest sõltuvad (või seda vähem on nende kahe klassi vahel seoseid).

Pärilus

Pärilus (inheritance) tähendab sarnaste klasside võimekust omada struktuurset ühisosa ehk baasklassi.

Polümorfism

Mõiste polümorfism (polymorphism) tuleb kreeka keelest, kus see tähendab mitut kuju või vormi. Objekt-orienteeritud programmeerimises tähendab see seda, et tänu pärilusele võib sama klass esineda mitmel erineval kujul.