La realizzazione di un'applicazione software secondo la metodologia OOP consiste di un insieme di oggetti che comunicano tra loro ed il più delle volte l'analisi dell'architettura rivela una fitta maglia di interdipendenze tra le classi anche tra quelle poste a livelli di astrazione differente. In realta questa situazione è l'effetto di una cattiva progettazione e comporta una serie di vincoli e di svantaggi di difficile soluzione.

Le numerose dipendenze impediscono il riuso di parti del codice in altri contesti, poiché diventa quasi impossibile isolare le classi di interesse dal resto dell'applicazione; anche le modifiche risultano complesse poiché i cambiamenti ad una classe possono comportare ulteriori cambiamenti ad un numero imprecisato di altre classi o generare malfunzionamenti su altre parti del codice.

Il principio di inversione della dipendenza è una regola che aiuta a ridurre le dipendenze tra le classi.

Prosegue...

Vota questo articolo per primo

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Quando si inizia larealizzazione di un nuovo progetto software, spesso è necessarioaffrontare il dilemma se sia più conveniente aggiungere nuovefunzionalità a del codice già esistente e ben testato, oppureriscrivere l'applicazione da zero.

Generalmente si tende ariscrivere da zero gran parte del codice esistente, anche se questocomporta una grande mole di lavoro e la necessità di testarenuovamente la correttezza dei programmi generati.

Prosegue...

Vota questo articolo per primo

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

lsp Il principio di sostituibilità di Liskov si propone di chiarire cosa significhi "derivare" un sottotipo:

B è un sottotipo di A se e solo se, per ogni programma che usi oggetti di classe A, posso utilizzare al loro posto oggetti di classe B e lasciare immutato il comportamento "logico" del programma.

In generale, le funzioni che utilizzano puntatori e riferimenti ad una classe base, dovrebbero essere in grado di utilizzare istanze di classi derivate dalla classe base senza conoscerne il tipo.  Per rispettare questo principio è necessario progettare le gerarchie di classi in modo tale che sussistano delle corrette relazioni IS-A. E' importante sottolineare che la relazione IS-A attiene al comportamento, cioè il comportamento della classe derivata deve essere consistente con quello della classe base.

 

Figura 1: Esempio di relazione tra Rettangolo e Quadrato

 

Ad esempio potremmo considerare il quadrato come un tipo particolare di rettangolo e quindi modellare le due classi come rappresentato in figura figura 1; in realtà questa scelta potrebbe non rivelarsi del tutto appropriata poichè le due classi non sono coerenti dal punto di vista del loro comportamento. Supponiamo, infatti, che la classe Rettangolo abbia i metodi necessari per impostare e leggere la lunghezza della base e dell'altezza, queste funzioni non sono corrette perla classe quadrato ed il loro comportamento deve essere modificato in modo tale l'impostazione di una delle due grandezze modifichi anche l'altra (vedi il listato 1).

001
002public class Rectangle
003{
004 private Point topLeft;
005 private double width;
006 private double height;
007
008 public virtual double Width
009 {
010  get { return width; }
011  set { width = value; }
012 }
013
014 public virtual double Height
015 {
016  get { return height; }
017  set { height = value; }
018 }
019}
020
021public class Square : Rectangle
022{
023
024 public override double Width
025 { 
026  set{base.Width = value;base.Height = value;}
027 }
028
029 public override double Height
030 {
031  set{base.Height = value;base.Width = value;}
032 }
033}

Purtroppo, sebbene questo codice sia coerente dal punto di vista del comportamento matematico di un quadrato, potrebbe non coincidere con ciò che si aspettano le funzioni client; ad esempio si supponga di avere la seguente funzione del listato 2.

001void g(Rectangle r)
002{
003 r.Width = 5;
004 r.Height = 4;
005 if(r.Area() != 20)throw new Exception("Bad area!");
006}

L'autore di questa funzione ha assunto che la modifica di una delle due grandezze lasci l'altra invariata e quindi la funzione g ritornerà un errore nel caso in cui gli sia passato un quadrato! Questa è un'evidente violazione del principio di Liskov, poiché la funzione non è in grado di trattare allo stesso modo sia i rettangoli che i quadrati.

Fondamentalmente dobbiamo usare l'ereditarietà (pubblica) solo quando estendiamo una classe, al contrario non possiamo utilizzarla per restringere la classe base (come nel caso del Quadrato, più restrittivo di un Rettangolo), altrimenti non potremmo usare la classe derivata (più ristretta) ovunque si possa usare la classe base (più generale).

Spesso l'ereditarietà è utilizzata come un comodo meccanismo per il riutilizzo del codice: il codice comune ad un insieme di classi è confinato in una classe, che rappresenta la base della gerarchia, da cui sono derivate tutte le altre sotto classi la cui implementazione sfrutta le funzioni della classe base. Questa scelta progettuale, sebbene molto comoda, è in generale sbagliata poiché l'ereditarietà non è utilizzata per modellare una relazione IS-A o WORKS-LIKE-A, ma la relazione IMPLEMENTED-IN-TERMS-OF. In questo genere di situazioni è preferibile sfruttare la composizione e non l'ereditarietà; al posto della composizione può essere utilizzata l'ereditarietà privata, ma solo quando sia strettamente necessaria (ad esempio per accedere ai membri protetti, oppure per sovrascrivere una funzione virtuale della classe padre).

Vota questo articolo per primo

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Calendario

<<  settembre 2010  >>
lumamegivesado
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910

View posts in large calendar
Licenza d'uso
Eccetto dove diversamente specificato, i contenuti di questo sito sono rilasciati mediante:

Licenza Creative Common