1.4-Software-Development-Principles

De staat van referentie klassen

Introductie

Natuurlijk ontwerp je jouw klassen op de manier zoals je dat geleerd hebt bij Objectgeorienteerd Programmeren, en je zorgt er voor dat alle variabelen private zijn, en je setters accepteren alleen de juiste waarden.

Maar, nu dat je complexere klasse structuren gaat gebruiken, moet je je bewust zijn van de interacties.

Gesorteerde data die veranderd kan worden

Laten we uit gaan van ee klassie die data gesorteerd opslaat. Iedere keer wanneer je een nieuw element toevoegt aan de lijst, wordt het op de juiste plek geplaats met een slim algoritme. De data die we hier opslaan heeft bijvoorbeeld een numerieke prioriteit. (De overige informatie is niet belangrijk.)

Hier komt het gevaar… Laten we er vanuit gaan dat de klasses publieke methoden aanbiedt die de prioriteit van een object kunnen veranderen.

In dit voorbeeld gaan de we “interne staat” van element 2 veranderen zodat dit element een andere positie had moeten hebben.

[ (3), (7), (5), (13) ]

Vraag: Blijft jouw opgeslagen lijst gesorteerd na dat de data gewijzigd is?

Antwoord: Nope!

Oplossingen

classDiagram

    class ListOwner {
        -myList : List~String~
        +getList() List~String~
    }

    class List {
        +addItem() void
        +replaceItem() void
        +removeItem() void
    }

    class OtherClass {
        +changeTheList() void
    }

    ListOwner --> List

    List <-- OtherClass

In de situatie beschreven hierboven is er geen mogelijkheid om de lijst altijd gesorteerd te houden. We kunnen er alleen op hopen dat mensen die we toegang geven tot de lijst geen rare dingen doen.

De ListOwner klasse bewaart de referentie naar zijn lijst en weet hoe elementen toegevoegd en verwijderd moeten worden. Dat komt omdat in het ontwerp van deze klasse alle interne implementaties er zorg voor dragen dat de lijst gesorteerd blijft. Maar een externe partij (OtherClass) heeft die kennis (en verantwoordelijkheid) niet.

Het enige wat een externa partij hoeft te weten is dat ze toegang krijgen tot een List klasse die de mogelijkheid biedt om elementen te veranderen. En dat zullen ze waarschijnlijk ook willen doen. Er is helaas geen manier om te garanderen dat deze externe partij geen ongewenste veranderingen doorvoert.

Geen externe toegang aanbieden.

Als jouw List referentie een publieke interface aanbiedt die de volgorde van elementen kan veranderen, dan zit er niks anders op dan de lijst prive (private) te houden. Dat niet alleen ook de elementen uit je lijst mogen niet vanaf buiten te bereiken zijn.

Om alle problemen te voorkomen weet niemand van de lijst. Externe partijen kunnen de lijst en de opgeslagen data nooit zien.

classDiagram
    class ListOwner {
        -myList : List~String~
        -getList() List~String~
    }

Nadeel: Externe partijen kunnen de inhoud van je lijst niet zien.

Geeft externe partijen een kopie.

Als externe partijen de lijst moeten kunnen inzien, en je wil niet dat jouw data gewijzigd worden, dan kun je een kopie maken van de lijst en alle elementen en die aanbieden aan iedereen die interesse heeft.

Als de externe partij hun kopie wijzigen, dan blijft jouw originele data ongewijzigd.

classDiagram
    class ListOwner {
        -myList : List~String~
        -getCopyOfList() List~String~
    }

    ListOwner --> MyList

    MyList .. YourList : copy of

    YourList <-- OtherClass

Nadeel: Als de externe partij wijzigingen maakt, dan weet jij van niks.

Alleen wijzigingen toestaan via JOUW interface.

Om controle te houden over de manier waarop elementen gewijzigd kunnen worden zou je, als je dat echt wil, een eigen set van methoden aanbieden aan externe partijen. Op deze manier vertellen gebruikers wat ze willen aanpassen, en jouw klasse kan op de juiste manier de integriteit van jouw lijst waarborgen. Je kunt, bijvoorveeld, eerst het aan te passen element verwijderen, dan aanpassen en dan terugplaatsen.

classDiagram

    class ListOwner {
        -myList : List~String~
        -getList() List~String~
        +addItem() void
        +replaceItem() void
        +removeItem() void
        +getItemAt(index : int)
        +???()
    }

    class OtherClass {
        +changeTheList() void
    }

    ListOwner <-- OtherClass

Nadeel: Jij moet alle methoden schrijven die de lijst (en de elementen daarin) aanpassen. Voor de tweede keer.

Schoonmaken naderhand.

Als je je gebruikers volledige toegang wil bieden, en de bovenstaande oplossingen zie je niet zitten, dan kun je enkel nog alles oplossen nadat de gebruiker klaar is.

Vlak voordaat de ListOwner de data zelf gaat gebruiken, moet je ongewenste data (zoals null waarden) zelf verwijderen en dan de lijst opnieuw sorteren.

Samenvatting

Externe toegang tot jouw interne datastructuren kan vanwege referenties er gevaarlijk zijn. Zelfs wanneer je goed hebt nagedacht over encapsulatie, moet je bewust blijven van de interacties tussen jouw ontwerpen.