1. Regole, princìpi e tecniche di
programmazione
Regole e princìpi applicabili
programmazione Orientata agli Oggetti
con esempi in C#
Andrea Colleoni - 2011
2. Princìpi (1)
Classi/Oggetti
Information Hiding (incapsulamento)
Ereditarietà e Composition over inheritance
Design by contract: interfacce e polimorfismo
Identificazione di precondizioni, postcondizioni e invarianti
Asserzioni
Defensive programming
Può essere perseguita tramite il Design By Contract
Separation of concerns
Ogni classe deve avere UNA SOLA responsabilità
Ogni frammento di codice DEVE AVERE UNA SOLA GIUSTA
POSIZIONE dove può essere collocato
DRY / DIE: Don’t Repeat Yourself / Duplication Is Evil (Single Source Of
Truth)
YAGNI: "You ain't gonna need it“ se una funzionalità non serve, è inutile
aggiungerla
Le entità progettate devono essere APERTE per l’estensione e CHIUSE
per la modifica
3. Princìpi (2)
Rasoio di Occam (correlato anche al KISS principle): la soluzione
più semplice solitamente è la migliore
Principio di Pareto: spesso l’80% dei fenomeni è dovuto al 20%
delle cause
Legge di Demetra (disaccoppiamento): ogni oggetto deve
comunicare SOLO con:
Se stesso
I propri parametri
Gli oggetti che ha creato
Gli oggetti figli
Principio della minore conoscenza: A component should take on
no more knowledge about another than is absolutely necessary to
perform an inherent concern.
Principio del minor privilegio: classi e metodi devono essere il più
STATICI e il più PRIVATI possibile
Principio di sostituzione di Liskov: una sottoclasse deve essere
completamente e trasparentemente sostituibile alla sua
superclasse
Complessità ciclomatica: la profondità del branching (nesting)
4. Princìpi: esempi (1)
Incapsulamento
Auto properties (NB: campo privato inaccessibile)
public string Name { get; set; }
Properties (rimane accessibile il campo)
private string _name;
public void setName(string value) {}
public string getName() {}
5. Princìpi: esempi (2) public struct CoOrds
Struct: crea un Value Type {
Quando è passato a d un metodo public int x, y;
viene copiato
Non ha costruttore di default public CoOrds(int p1, int p2)
{
x = p1; y = p2;
}
}
Ereditarietà
Abstract: consente di non fornire public class A
l’implementazione {
Virtual: dichiara la possibilità di fare public A()
l’override {}
Sealed: dichiara la volontà di arrestare }
la catena dell’ereditarietà (non può public class B : A
quindi essere abstract) {
public B() { }
New: può servire per nascondere un
}
membro di una super classe
L’estensione riguarda UNA SOLA classe
concreta
6. Override e New: esempio
// Define the base class Car car1 = new Car();
class Car car1.DescribeCar();
{ System.Console.WriteLine("----------");
public virtual void DescribeCar() ConvertibleCar car2 = new ConvertibleCar();
{ car2.DescribeCar();
System.Console.WriteLine("Four wheels and an engine."); System.Console.WriteLine("----------");
} Minivan car3 = new Minivan();
} car3.DescribeCar();
// Define the derived classes System.Console.WriteLine("----------");
class ConvertibleCar : Car
{
public new virtual void DescribeCar()
{ Car[] cars = new Car[3];
base.DescribeCar(); cars[0] = new Car();
System.Console.WriteLine("A roof that opens up."); cars[1] = new ConvertibleCar();
} cars[2] = new Minivan();
}
class Minivan : Car foreach (Car vehicle in cars)
{ {
public override void DescribeCar() System.Console.WriteLine("Car object: " + vehicle.GetType());
{ vehicle.DescribeCar();
base.DescribeCar(); System.Console.WriteLine("----------");
System.Console.WriteLine("Carries seven people."); }
}
}
7. Princìpi: esempi (3)
public class A
Plimorfismo {
public virtual void DoWork() { }
Consente di trattare usare un oggetto
}
public class B : A
come se fosse del tipo della sua {
public override void DoWork() { }
superclasse/interfaccia }
public class C : A
{
public override void DoWork() { }
}
A x = new A();
A y = new B();
A z = new C();
8. Princìpi: esempi (4)
Legge di Demetra : Principle of Least Knowledge
public class Paperboy public class Customer public class Wallet
{ { {
decimal fundsCollected; public Customer() : this(null) { } public Wallet(decimal money)
public Paperboy() public Customer(Wallet wallet) {
{ { Money = money;
Customers = new List(); Wallet = wallet; }
} } public decimal Money { get; set; }
public List Customers { get; set; } public Wallet Wallet { get; set; } }
// ... }
} public decimal MakePayment(decimal amount)
public void CollectPayments() {
{ if (Wallet.Money >= amount)
// Paper costs $2.00 {
decimal payment = 2m; Wallet.Money -= amount;
foreach (Customer customer in Customers) return amount;
{ }
if (customer.Wallet.Money >= payment) return 0m;
{ }
customer.Wallet.Money -= payment;
decimal payment = customer.MakePayment(charge);
fundsCollected += payment;
if (payment != 0m)
}
{
}
fundsCollected += payment;
}
}
9. Princìpi: esempi (5)
Principio di sostituzione di Liskov
public class ElectricDuck : IDuck
public interface IDuck {
{ public void Swim()
void Swim(); {
} if (!IsTurnedOn)
return;
//swim logic
public class Duck : IDuck
}
{
} Client Code
public void Swim()
{ void MakeDuckSwim(IDuck duck)
//do something to swim {
} if (duck is ElectricDuck)
} ((ElectricDuck)duck).TurnOn();
void MakeDuckSwim(IDuck duck)
duck.Swim();
{
}
duck.Swim();
}
Diverse implementazioni Cambio Swim() in
di Duck si comportano ElectricDuck per
diversamente Ha un problema rispettare
Open/Closed: tutti i client l’interfaccia
devono cambiare
10. Memento: design di classi
Single responsibility principle
S SRP
the notion that an object should have only a single responsibility.
Open/closed principle
O OCP the notion that “software entities … should be open for extension,
but closed for modification”.
Liskov substitution principle
the notion that “objects in a program should be replaceable with
L LSP
instances of their subtypes without altering the correctness of that
program”. See also design by contract.
Interface segregation principle
I ISP the notion that “many client specific interfaces are better than one
general purpose interface.”
Dependency inversion principle
the notion that one should “Depend upon Abstractions. Do not
D DIP
depend upon concretions.”
Dependency injection is one method of following this principle.
11. Refactoring
Quando i princìpi precedentemente esposti sono violati il codice
sorgente va rifattorizzato
Obiettivi generali sono il perseguimento dei princìpi; in sintesi si
devono perseguire i princìpi dell’OOP
Aumento di astrazione
Incapsulamento, Generalizzazione di Tipo, Uso di Strategy, Sostituzione di
branching con plimorfismo
Suddivisione in frammenti piccoli
Estrazione di metodi, Estrazione di classi
Collocazione e denominazione
Spostamento e/o Rinomina di campi, proprietà metodi, PullUp e PushDown
Code review: è la disamina sistematica del codice sorgente di un
progetto
Non deve essere fatto da chi ha scritto il codice originario
Può essere aiutato da sistemi automatici di analisi statica del
codice (e.g. StyleCop, SourceMonitor, etc.)
Va fatta periodicamente e la presenza di questa attività deve
essere prevista nel processo di sviluppo
12. Memento: namespaces
I primi tre principi riguardano la cohesion del namespace, ci dicono cosa inserire
(e per contro, cosa non inserire) in un namespace:
The Release Reuse The granule of reuse is the granule
REP
Equivalency Principle of release.
The Common Closure Classes that change together are
CCP
Principle packaged together.
Classes that are used together are
CRP The Common Reuse Principle
packaged together.
Gli altri tre riguardano i couplings tra namespaces, e metriche che valutano la
naming structure del sistema.
The Acyclic Dependencies The dependency graph of packages
ADP
Principle must have no cycles.
The Stable Dependencies
SDP Depend in the direction of stability.
Principle
The Stable Abstractions
SAP Abstractness increases with stability.
Principle
13. References
[McConnell]: Steven McCollen - Code Complete
2° edition - http://www.cc2e.com/
[GoF]: Gamma, Erich; Richard Helm, Ralph
Johnson, and John Vlissides (1995). Design
Patterns: Elements of Reusable Object-Oriented
Software. Addison-Wesley
[Fowler] Fowler, Martin (2002). Patterns of
Enterprise Application Architecture. Addison-
Wesley