Inleiding
Robert L. Glass betoogde dat praktijkervaring belangrijker is dan theorie. Softwareontwikkeling is een typisch voorbeeld van een vakgebied waar de praktijk voorloopt op de theorie. Ontwikkelaars halen de meeste kennis op als ze hun handen vuilmaken met concrete code. Daarom laten we de theorie en concepten even voor wat ze zijn en bekijken we een eenvoudig programma.
Een ticketverkoop applicatie implementeren
- We willen een klein evenement organiseren ter promotie van een klein theater.
- Evenementinhoud: Via een loting worden bezoekers geselecteerd die een gratis uitnodigingskaart ontvangen voor de voorstelling.
- We moeten bezoekers die gewonnen hebben en die verloren hebben scheiden.
- Bezoekers die gewonnen hebben: ruilen de uitnodigingskaart in voor een ticket.
- Bezoekers die verloren hebben: kopen een ticket met geld.
Wat is er mis?
Robert Martin legt uit welke drie functies een softwaremodule (ongeacht de grootte, zoals een klasse, pakket of bibliotheek, een willekeurig onderdeel dat een programma vormgeeft) moet hebben.
- Werkt correct tijdens de uitvoering.
- Bestaat om te worden aangepast.
- Aanpassingen moeten met minimale inspanning mogelijk zijn.
- Communiceert met mensen die de code lezen.
- Ontwikkelaars moeten de code gemakkelijk kunnen lezen en begrijpen.
De eerder genoemde ticketverkoop applicatie voldoet aan de eerste voorwaarde (correct functioneren), maar voldoet niet aan de doelen van aanpassingsgemak en communicatie.
Onverwachte code
Laten we eens kijken waarom de code niet voldoet aan het doel van communicatie.
- Wat doet de enter() methode van de Theater klasse?
- Het theater controleert de tas van de bezoeker om te zien of er een uitnodigingskaart in zit.
- Als er een uitnodigingskaart in de tas zit, vraagt het theater de verkoper om een ticket uit de kassa naar de tas van de bezoeker te verplaatsen.
- Als er geen uitnodigingskaart in de tas zit, haalt het theater geld uit de tas van de bezoeker om een ticket te kopen, en stopt het ticket in de tas.
- Vanuit het perspectief van de bezoeker moet hij toezien hoe een derde partij (het theater) zijn tas doorzoekt, geld meeneemt en een ticket erin stopt.
- Vanuit het perspectief van de verkoper moet hij toezien hoe een derde partij (het theater) zonder toestemming de kassa manipuleert met tickets en geld.
- Begrijpelijke code is code waarvan de werking niet sterk afwijkt van onze verwachtingen. Het bovenstaande theatergedrag wijkt af van onze verwachtingen.
- De bezoeker moet zelf geld uit zijn tas halen en dit aan de verkoper geven om een ticket te kopen.
- De verkoper moet zelf een ticket uit de kassa halen en dit aan de bezoeker geven, en het geld van de bezoeker ontvangen en in de kassa bewaren.
- Om de enter() methode te begrijpen, moet je bovendien veel details onthouden.
- Audience heeft een Bag object.
- Bag bevat geld en tickets.
- TicketSeller verkoopt tickets vanuit TicketOffice en TicketOffice bewaart geld en tickets.
Code die gevoelig is voor wijzigingen
De enter() methode gaat uit van twee voorwaarden.
- Bezoekers dragen altijd een tas bij zich om geld en uitnodigingskaarten in te bewaren.
- Verkopers verkopen tickets alleen vanaf de kassa.
Maar hoe zit het dan met de volgende situaties?
- Bezoekers hebben mogelijk geen tas bij zich.
- Bezoekers kunnen met een creditcard betalen in plaats van met contant geld.
- Verkopers kunnen tickets ook buiten de kassa verkopen.
Als we bijvoorbeeld aan de eerste eis willen voldoen, moeten we het Bag object van Audience verwijderen en de enter() methode van de Theater klasse aanpassen. Dit komt omdat de Theater klasse te veel afhankelijk is van details, zoals dat bezoekers een tas dragen en verkopers alleen tickets vanaf de kassa verkopen. Als er ook maar één van deze details verandert, moeten zowel de betreffende klasse als de afhankelijke klassen (bijv. Theater) worden aangepast.
Dit is een probleem dat te maken heeft met de afhankelijkheden tussen objecten, en afhankelijkheid impliceert impact bij wijzigingen. Maar het doel van objectgeoriënteerd ontwerp is het creëren van een gemeenschap van objecten die samenwerken en van elkaar afhankelijk zijn. Dus we moeten niet zomaar alle afhankelijkheden elimineren, maar alleen de minimale afhankelijkheden behoudenen onnodige afhankelijkheden verwijderen.
Als de afhankelijkheid tussen objecten te sterk is, spreken we van een hoge koppelingen hoe hoger de koppeling tussen twee objecten, hoe groter de kans dat ze samen moeten worden gewijzigd. Daarom is het doel van het ontwerp om de koppeling tussen objecten te verminderen, zodat wijzigingen gemakkelijker kunnen worden doorgevoerd.
Ontwerp verbeteren
Theater hoeft niet te weten of een bezoeker een tas bij zich heeft of dat een verkoper tickets vanaf de kassa verkoopt. Theater wil alleen dat bezoekers het theater betreden. Daarom moeten we bezoekers in staat stellen om zelf geld en uitnodigingskaarten uit hun tas te beheren, en verkopers in staat stellen om zelf tickets en de verkoopprijs vanaf de kassa te beheren, als zelfstandige entiteiten.
Zelfstandigheid vergroten
Objecten die alleen nauw verwante taken uitvoeren en niet-gerelateerde taken delegeren aan andere objecten, worden beschouwd als objecten met een hoge cohesie. Door zelfstandige objecten te creëren die hun eigen gegevens beheren, kunnen we de koppeling verminderen en de cohesie vergroten.
Procedureel versus objectgeoriënteerd
- Procedureel
- De enter() methode van Theater is een proces en Audience, TicketSeller, Bag en TicketOffice zijn gegevens.
- De benadering waarbij processen en gegevens in aparte modules worden geplaatst, wordt procedurele programmering genoemd.
- Er is veel code die in tegenspraak is met onze intuïtie. (bijv. Bezoekers beheren zelf geld en uitnodigingskaarten.)
- Het is moeilijk om de impact van gegevenswijzigingen te beperken.
- Verantwoordelijkheden worden centraal beheerd. (Theater beheert alles)
- Objectgeoriënteerd
- De benadering waarbij gegevens en processen in dezelfde module worden geplaatst, wordt objectgeoriënteerde programmering genoemd.
- We kunnen code schrijven die overeenkomt met onze intuïtie.
- De impact van gegevenswijzigingen kan effectief worden beperkt door middel van encapsulatie.
- Elk object is zelf verantwoordelijk voor zijn eigen taken.
Het kan nog beter
- De Bag klasse van de Audience klasse is nog steeds een passief object dat wordt aangestuurd door de Audience klasse. We moeten de Bag klasse omvormen tot een zelfstandig object.
- Ook de TicketOffice van de TicketSeller klasse wordt willekeurig beheerd door TicketSeller. We moeten TicketOffice omvormen tot een zelfstandig object.
- Na deze wijziging is er echter een extra koppeling ontstaan tussen TicketOffice en Audience.
- Zoals je ziet, moet je bij het ontwerpen afwegingen maken. In dit geval kunnen we ervoor kiezen om TicketOffice enigszins passief te maken om de koppeling met Audience te verminderen.
Nee, het is gelogen!
- In de echte wereld veranderen alle objecten, zelfs de passieve, in actieve en zelfstandige objecten zodra ze de wereld van objectgeoriënteerd ontwerp betreden.
- Het is handig om personificatie te gebruiken en passief objecten te zien als entiteiten die lachen, praten en boos worden.
Objectgeoriënteerd ontwerp
Waarom is ontwerp nodig?
- Ontwerp is het ordenen van code.
- Goed ontwerp is ontwerp dat de huidige functionaliteit volledig vervult en tegelijkertijd toekomstige aanpassingen soepel kan opvangen.
Objectgeoriënteerd ontwerp
- Aanpasbare code is begrijpelijke code.
- Het objectgeoriënteerde paradigma helpt ons om code te schrijven zoals we de wereld zien.
- Objecten zijn zelfstandige entiteiten die verantwoordelijk zijn voor hun eigen gegevens.
- Goed objectgeoriënteerd ontwerp is ontwerp dat de afhankelijkheden tussen samenwerkende objecten op de juiste manier beheert.
Bron
- Objecten
Reacties0