Toon posts:

Liskov substitution principle en Open/Closed principle

Pagina: 1
Acties:

Verwijderd

Topicstarter
Beste mede-tweakers,

Ik ben mijn brein al enkele uren aan het breken maar kom er niet uit. Ik wil op 2 packages van een Java project het LSP en OCP toepassen maar loop vast op een vraag.

Situatie
In het project bevindt zich deze abstracte base klasse:

Java:
1
2
3
4
5
6
7
8
abstract class Compass extends Observable
{
   private double bearingvalue = 0.0;    
   public void stop() {}   
   public boolean isConnected() { return true; }
   public double getBearingvalue() { return bearingvalue; }
   public void init() {   }
}


Dit is een basis interface om bepaalde waarden uit een hardwarematig of gesimuleerd kompas te lezen. Er is een implementatie gemaakt , genaamd COMPortCompass, die de compoort gebruikt om e.e.a. uit te lezen. Dit is naar mijn mening prima. Het probleem zit hem nu in de 2de klasse, genaamd CompassSimulator. Deze klasse heeft de volgende inhoud:

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class CompassSimulator extends Compass
{
   private sena.simulator.Facade simulator;
   public CompassSimulator() {
      simulator = new sena.simulator.Facade();
      simulator.initialize();
      simulator.setHeadingEngine(true, this);
   }
   public void setBearing(double bearing){
      bearingvalue = bearing;
      setChanged();
      notifyObservers("UPDATE_BEARING");
   }

}


Voor meer informatie het klassediagram van de package:
http://www.digatech.nl/senacompass.jpg

Probleemomschrijving
Mijn vraag nu is, is dit geen schending van het liskov substitution principle? Ik denk van niet en dat het initialiseren van een bepaalde interface meer een slechte ontwerpbeslissing is dan dat het te maken heeft met LSP. Zoals ik kan beoordelen veranderen de 'aan te nemen' impliciete precondities ook niet.

Alvast bedankt,

Michel

  • JKVA
  • Registratie: Januari 2004
  • Niet online

JKVA

Design-by-buzzword fanatic

Ten eerste zie ik geen verandering in gedrag. Bestaande code (dus code die methoden uit de superklasse aanroept) zal niet zomaar omvallen. Het zou anders zijn als isConnected() ineens op vreemde momenten false zou returnen.

Bestaande code die bijv. init aanroept, kan dat in de toekomst ook doen, maar achter de schermen gebeurt gewoon niets. Aan de andere kant, misschien dat de constructor nu een Exception kan gooien die in de oude versie niet gegooid zou worden.

Bovendien, zonder contract kun je eigenlijk niets kapotmaken. Je clients weten immers niet wat het contract van je klasse is. Je voldoet dus altijd aan LSP. (of juist nooit. Het is maar hoe je het ziet)

Dus nee, naar mijn idee is het geen LSP violation.

Fat Pizza's pizza, they are big and they are cheezy


  • BCC
  • Registratie: Juli 2000
  • Laatst online: 17:37

BCC

Ik zie ook niet waarom het een violation zou zijn.
offtopic:
Voor hierboven: coden by contract is ranzig, schrijf gewoon specs/tests als je zeker wil zijn dat iets blijft werken.

[ Voor 23% gewijzigd door BCC op 02-06-2008 21:22 ]

Na betaling van een licentievergoeding van €1.000 verkrijgen bedrijven het recht om deze post te gebruiken voor het trainen van artificiële intelligentiesystemen.


  • JKVA
  • Registratie: Januari 2004
  • Niet online

JKVA

Design-by-buzzword fanatic

Wat ik bedoelde met mijn design by contract opmerking is dat de code van de TS geen JavaDoc of iets dergelijks bevat. Daardoor is het niet mogelijk om goed aan te geven of deze code het LSP overtreedt.

Over tests, ik zie een test ook als documentatie, dus onderdeel van het contract. Hetzelfde geldt voor de specs.

Fat Pizza's pizza, they are big and they are cheezy


  • BCC
  • Registratie: Juli 2000
  • Laatst online: 17:37

BCC

Dan heb ik niets gezegd :)

Na betaling van een licentievergoeding van €1.000 verkrijgen bedrijven het recht om deze post te gebruiken voor het trainen van artificiële intelligentiesystemen.


  • whoami
  • Registratie: December 2000
  • Laatst online: 22:26
Het kan aan mij liggen, maar ik zie nergens het LSP toegepast ?
LSP zegt :
Als S een subtype is van T, dan mogen objecten van type T vervangen worden door objecten van type S zonder dat er implementatie-wijzigingen moeten gebeuren

Het enige waar ik aan kan denken waar het LSP gebruikt wordt, is in je 'facade'. Als je daar enkel refereert naar 'Compass', dan heb je geen violation.

https://fgheysels.github.io/


  • JKVA
  • Registratie: Januari 2004
  • Niet online

JKVA

Design-by-buzzword fanatic

Ik ging ervan uit dat de TS doelde op de Compass en CompassSimulator klasse. Maar idd, Compass is abstract, dus LSP is misschien niet van toepassing. Had die abstract niet gezien.

Maar het hangt nogmaals af van de semantiek van de klasse. En zonder docs kan ik niet zien wanneer iets een violation is.

Fat Pizza's pizza, they are big and they are cheezy


  • Confusion
  • Registratie: April 2001
  • Laatst online: 01-03-2024

Confusion

Fallen from grace

JKVA schreef op dinsdag 03 juni 2008 @ 09:58:
Ik ging ervan uit dat de TS doelde op de Compass en CompassSimulator klasse. Maar idd, Compass is abstract, dus LSP is misschien niet van toepassing. Had die abstract niet gezien.
Het lijkt me dat dat nog steeds prima van toepassing is. LSP gaat over de vraag of er in je code iets moet veranderen als je een variabele als "Compass compass" wijzigt in "CompassSimulator compass". Dat geldt net zo goed voor benodigde compile time aanpassingen (waarin compass in het eerste geval als type 'Compass' beschouwt wordt), als voor runtime aanpassingen (waarin compass ook in het eerste geval type 'CompassSimulator' zou kunnen zijn).

Wie trösten wir uns, die Mörder aller Mörder?


Verwijderd

Topicstarter
whoami schreef op dinsdag 03 juni 2008 @ 09:39:
Het kan aan mij liggen, maar ik zie nergens het LSP toegepast ?
LSP zegt :
Als S een subtype is van T, dan mogen objecten van type T vervangen worden door objecten van type S zonder dat er implementatie-wijzigingen moeten gebeuren

Het enige waar ik aan kan denken waar het LSP gebruikt wordt, is in je 'facade'. Als je daar enkel refereert naar 'Compass', dan heb je geen violation.
Klopt, maar LSP gaat ook over de aannamen die een client kan doen die de code gaat schrijven. Het kan heel subtiel zijn. In mijn voorbeeld schaadt de klasse Compass het LSP waarschijnlijk omdat isConnected() geen methode is van een compas als er een implementatie is van een SimulatieCompass. Een simulatie van een kompas is het onderscheidt tussen een softwarematig kompas en een hardwarematig kompas ( binnen de context van het project ) . De programmeur heeft dus aannamen over de methode isConnected() ( nogmaals, erg subtiel ) . isConnected hoort gewoon niet bij een abstracte klasse van een kompas want een kompas kan op meerdere manieren geimplementeerd worden ( hardware/software ) binnen het project. Het is daarom een schending omdat de aanname die gedaan kan worden niet terecht is.

Er is geen design contract geformuleerd omdat het stel prutsers dit nooit heeft begrepen en het is niet mijn taak omdat nu te gaan doen. Wel kan er rekening gehouden worden met de context en het 'impliciete' design contract. Robert C Martin beschrijft dit goed in het artikel waaruit jij waarschijnlijk die quote ook hebt.

Ik heb het er vandaag met verschillende docenten over gehad. Hoewel het subtiel is en er vanalles over te zeggen is. Het is geen goede code. Ik heb de oplossing voor mijzelf nu helder en kan deze uitwerken.

Topic kan naar mijn mening gesloten worden , of het moet zich gaan uiten als een discussie over Liskov algemeen :)

  • Confusion
  • Registratie: April 2001
  • Laatst online: 01-03-2024

Confusion

Fallen from grace

Verwijderd schreef op dinsdag 03 juni 2008 @ 19:12:
Klopt, maar LSP gaat ook over de aannamen die een client kan doen die de code gaat schrijven. Het kan heel subtiel zijn. In mijn voorbeeld schaadt de klasse Compass het LSP waarschijnlijk omdat isConnected() geen methode is van een compas als er een implementatie is van een SimulatieCompass.
Er is dan de implementatie die altijd 'true' teruggeeft. Als dat gesimuleerde kompas het echte kompas zo goed mogelijk moet benaderen, dan moet het ook te 'disconnecten' zijn. Een isConnected() methode heeft dus wel degelijk zin op een gesimuleerd kompas, ook als de implementatie in het simpelste geval een constante teruggeeft. Het zouden rare interfaces worden, als we onze interfaces gingen aanpassen aan het feit dat bij gesimuleerde en 'mock' objecten bepaalde properties constant/irrelevant zijn. Het gaat om de entiteiten in de werkelijkheid (tenzij je natuurlijk een simulator aan het schrijven bent; dan wordt het een ander verhaal).
isConnected hoort gewoon niet bij een abstracte klasse van een kompas want een kompas kan op meerdere manieren geimplementeerd worden ( hardware/software ) binnen het project. Het is daarom een schending omdat de aanname die gedaan kan worden niet terecht is.
Dan heeft init() eigenlijk ook in beide klassen een hele andere betekenis, want de ene keer is het initialisatie van wat variabelen, terwijl er de andere keer daadwerkelijk electromechanische gevolgen in de werkelijkheid zijn. Daarover doe je ook 'verschillende aannames'. Hetzelfde geldt voor stop(). Op die manier hou je niets over. Kern van OO is juist dat je geen aannames doet over de interne werking van methodes van objecten. Als isConnected() true teruggeeft, dan is het kompas verbonden. Welke betekenis dat verder in 'de werkelijkheid' heeft is niet relevant; op basis daarvan moet je verder kunnen, binnen de termen die gehanteerd worden. Achter het interface moet zowel een softwarematig als een hardwarematig kompas schuil kunnen gaan. Het lijkt me dat voor iedere machine, en dus voor iedere simulatie van een machine, de 'isAvailable()', 'isPoweredOn()' of 'isConnected()' methode essentieel is.

[ Voor 9% gewijzigd door Confusion op 03-06-2008 20:49 ]

Wie trösten wir uns, die Mörder aller Mörder?

Pagina: 1