Zasady programowania obiektowego

background image

Zasady programowania

obiektowego

Prostota przede wszystkim

background image

Zasady dla klas

background image

Law of Demeter

Demeter była boginią przyrody (Gleba-Matka), urodzajów, patronką

rolnictwa. Główny mit związany z Demeter opisuje jej poszukiwania zaginionej

córki (swojej i Zeusa) - Persefony (Kora - Dziewczyna). Bogini zostawiła ją na

łące, bawiącą się z nimfami (okeanidami). Odchodząc zabroniła jej zrywać

narcyzów - kwiatów związanych z bóstwami podziemnymi. Persefona nie

posłuchała matki. Z woli Zeusa wyrósł na łące wspaniały kwiat, w którym

z korzenia wyrastało sto łodyg o cudownym wyglądzie i zapachu. Gdy

dziewczyna odkryła kwiat zapomniała o przestrogach matki i zerwała go.

Otwarła się wówczas ziemia i Hades, bóg podziemi, porwał Persefonę

i zaczarowanym rydwanem wywiózł ją w dal. Lecieli ponad ziemią i morzem - do

Tartaru. Nikt nie słuchał krzyków Persefony. Dopiero po długim czasie dotarły

one do matki - Demeter.

• Zrozpaczona Demeter rozpoczęła poszukiwania. Przekazała swoją żałobę po

Persefonie polom. Zasiewy zmarniały, spiekota wysuszyła źródła i rzeki, padały

zwierzęta, a ludzi nawiedził głód. Nie było nawet z czego składać ofiar bogom.

Bogowie olimpijscy wystraszyli się, że jeżeli ludzie wyginą, to kto będzie składał

im ofiary. Zeus wysłał do Demeter muzy, charyty i kolejno innych olimpijczyków.

Ale nikomu nie udało się ubłagać bogini. W końcu Zeus wysłał Heraklesa do

Hadesa z żądaniem uwolnienia Persefony. Hades posłuchał rozkazu Zeusa, ale

rozstając się z Persefoną podał jej słodką pestkę granatu. Persefona zjadła ją,

nie wiedząc, że związała się tym na zawsze z podziemnym królestwem.

• Gdy Persefona wyszła z podziemi uradowana Demeter sprawiła, że wszystko na

ziemi rozkwitło. Z powodu zjedzonej pestki granatu Persefona odtąd trzecią

cześć roku musiała spędzać w królestwie Hadesa. Na ziemi panowała wtedy

zima - przerwa w wegetacji. Pozostałą część roku matka z córką spędzały

razem, obdarzając ziemię plonami.

background image

Prawo Demeter

background image

Law of Demeter

Law of Demeter (LoD) (Don't Talk to

Strangers; or Principle of Least Knowledge)

Treść: Niech obiekt rozmawia tylko z bliskimi

współpracownikami.

Opis: Chodzi w niej o to, żeby obiekt nie

komunikował się (wywoływał metod, pobierał

wartości pól) z obiektów w dalekiej odległości w

grafie powiązań między obiektami. Oznacza to, że

obiekt powinien mieć minimalną wiedzę o innych

obiektach. Unika się dzięki temu „kruchych”

połączeń, co ogranicza wpływ miejscowych zmian

w projekcie na resztę projektu.

background image

Law of Demeter – intuicja

Możesz się bawić:
• sam ze sobą,
• swoimi zabawkami (ale nie

wolno Ci rozbierać ich na części!),

• zabawkami, które dostaniesz od innych,
• zabawkami, które sam stworzysz, gdzie

zabawkami są po prostu obiekty, a
zabawa polega na wywoływaniu metod.

background image

Law of Demeter -

formalizacja

public class Demeter { // Prawo Demeter dla klas
private A a;
private int func() { ... }
public void example (B b) {
// obiekt może wywołać tylko takie metody:
int k = func(); // <--------- tego samego obiektu

b.invert(); // <------------- parametru przesłanego do funkcji

a = new A();
a.setActive(); // <---------- obiektu stworzonego przez

Demeter

}
}

background image

Law of Demeter -

naruszenie

class A {public: void m(); P p(); B b; };
class B {public: C c; };
class C {public: void foo(); };
class P {public: Q q(); };
class Q {public: void bar(); };
void A::m() {this.b.c.foo();

this.p().q().bar();}

background image

Zmniejszenie powiązań

między obiektami. Przykład.

• Kiepskie rozwiązanie dla wyszukiwania

wina

• W= ListaWin.dajWina();
• /* Wyszukiwanie liniowe w liście win... */
• Odwołujemy sie do pola klasy ListaWin.
• Nadopiekuńczość – chcemy wykonać

pracę za kogoś innego, bo na przykład
uważamy, że lepiej to zrobimy.

background image

Zmniejszenie powiązań

między obiektami. Przykład.

• Związane są z tym dwa potencjalne problemy:
• Klasa ListaWin może zawierać struktury

przyśpieszające wyszukiwanie win po nazwie,
w czasie logarytmicznym, dlatego liniowe
przeglądanie listy jest nieefektywne.

• Po pewnym czasie może się okazać, że

będziemy chcieli przechowywać listę win w
innej strukturze danych (np. w posortowanej
tablicy) i trzeba będzie przerabiać wszystkie
wyszukiwania win w programie.

background image

Korzyści ze stosowania LoD

Dwie główne korzyści, które daje nam

stosowanie prawa Demeter to:

• Nie musimy znać struktury innych

obiektów.

• Wszelkie zmiany w innych obiektach

nie maja wpływu na naszą metodę.

background image

Korzyści cd.

• Wnioski płynące ze stosowania prawa Demeter,

potwierdzone empirycznie podczas badań na
rzeczywistych programach wskazują, że ułatwia
ono pielęgnację, zmniejsza gęstość błędów, a
także ogranicza liczbę i rodzaj powiązań pomiędzy
obiektami, a co za tym idzie – wzmacnia
hermetyzację i abstrakcję klasy.

• Zastosowanie prawa Demeter na poziomie kodu

programu powoduje przeniesienie
odpowiedzialności za dostęp do metod i atrybutów
klasy z obiekt odwołującego się do nich do ich
właściciela.

background image

LoD króciutko

• Rumbaugh podsumował zasadę Law

of Demeter w następujący sposób:

Metoda powinna mieć ograniczoną
wiedzę o modelu obiektów.

background image

Cienie

• Konsekwentne stosowanie prawa

Demeter, może skutkować nadmierną
liczbą metod, których jedynym zadaniem
jest przedłużenie wywołania metody

• class Samochód{
• private Silnik silnik;
• public void uruchom() { silnik.uruchom;}
• }

background image

A co z tym zrobić?

• System.Console.Writeln(„Hello World”);
• Żeby prawo Demeter nie rodziło takich

sprzeczności, zezwala na odwoływanie
sie do pól i metod obiektów globalnych
(w szczególności do różnych
przestrzeni nazw; w Javie pakietów).

background image

Law of Demeter

Istnieją dwie postaci prawa Demeter:

silna i słaba.

W przypadku wersji słabej prawo to

jest rozszerzone także na podklasy
klasy analizowanej

background image

Single-Responsibility

Principle

• Zasada ta jest silnie powiązana z zasadą

skupienia (high cohesion) ze wzorów GRASP

(General Responsibility Assignment Software

Patterns ). Chodzi o to, żeby klasa miała

tylko jedną odpowiedzialność (responsibility),

ponieważ odpowiedzialność jest

potencjalnym źródłem zmian. Jeżeli klasa ma

więcej niż jedną odpowiedzialność to zmiana

jednej z nich będzie wpływać na

implementację reszty.

background image

Single-Responsibility

Principle

• Prostym przykładem złamania tej zasady

jest klasa, która jednocześnie zawiera logikę
(np. obliczanie powierzchni figury) oraz kod
związany z grafiką (np. odpowiedzialny za
rysowanie tej figury). Trochę lepszym
przykładem złamania zasady SRP jest klasa
zawierająca jednocześnie kod wybierający
dane z bazy oraz logikę odpowiedzialną za
obliczanie pozycji faktury (typowy i częsty
przykład pomieszania logiki biznesowej)

background image

Single-Responsibility

Principle

• class Suma

{

private int wynik;

public void Dodaj(int a, int b)

{

wynik = a + b;

}

public void Wypisz()

{

Console.WriteLine(wynik);

}

}

background image

Single-Responsibility

Principle

• interface IWyświetl

{

void Wyświetl(int wynik);

}

class Suma{

private int wynik;

public int Wynik

{

get

{

return wynik;

}

• }

public void Dodaj(int a, int

b)

{

wynik = a + b;

}

}

• class SumaUI : IWyświetl

{

public void Wyświetl(int

wynik)

{

Console.WriteLine(wynik);

}

}

//UI – User Interface

• class Program

{

static void Main(string[] args)

{

Suma s = new Suma();

s.Dodaj(2,2);

IWyświetl w = new

SumaUI();

w.Wyświetl(m.Wynik);

}

}

background image

Open/close principle

Treść: Elementy oprogramowania powinny być otwarte na

rozszerzenia, ale zamknięte na modyfikacje.

Opis: Podstawą dla tej zasady jest spostrzeżenie, że każdy kod (klasa,

moduł, itp.) zmienia się z czasem i ma więcej niż jedną wersję. Dobre

stosowanie tej zasady powoduje, że wprowadzenie rozszerzeń w jednej

klasie nie spowoduje całej kaskady zmian w innych klasach.

Trzeba zatem tworząc kod od razu mieć na uwadze możliwość

przyszłych rozszerzeń. Trzeba przewidzieć możliwość tworzenia

rozszerzeń i zmiany zachowania kodu, ale nie powinno się to odbywać

poprzez zmianę już istniejącego kodu.

Można powiedzieć, że OCP to główna zasada projektowania

obiektowego, która pozwala spełniać jego slogany reklamowe

(flexibility, reusability, maintainability).

Przykład: stosowanie się do tej zasady zazwyczaj oznacza bazowanie

na abstrakcji (klasy abstrakcyjne) i stosowanie polimorfizmu

(oczywiście nie tylko do tego). Dobrym przykładem złamania tej zasady

jest tworzenie logiki zawierającej instrukcje „switch..case”

rozróżniającej typy obiektów.

background image

Open/close principle

class

Shape

{

private

Punkt _center;

#region

Center Get / Set

}

class

Circle : Shape

{

private

int

_radius;

#region

Radius Get / Set

}

class

Square : Shape

{

private

int

_side;

#region

Side Get / Set

}

background image

Open/close principle

class

DrawManager

{

private

List

<

Shape

>

shapeList;

public

DrawManager(){…}

public

void

add(Shape s){…}

public

void

drawShapes(){->}

private

void

drawCirle(Circle c){..}

private

void

drawSquare(Square sq){..}

}

public

void

drawShapes()

{
foreach(Shape s

in

shapeList)

{

if

(s is Circle)

{

drawCirle((s as Circle));

}

else

if

(s is Square)

{

drawSquare((s as Square));

}
}
}

background image

Open/close principle

abstract

class

Shape

{

private

Punkt _center;

public

abstract

void

Draw();

};

class

Circle : Shape

{

private

int

_radius;

public

override

void

Draw()

}

class

Square : Shape

{

private

int

_side;

public

override

void

Draw()

}

class

DrawManager

{

//......

public

void

drawShapes()

{
foreach (Shape s

in

_shapeList)

s.Draw();
}

//......

}

background image

OCP - metody

• Podstawowymi mechanizmami

umożliwiającymi stosowanie zasady
OCP są abstrakcja i polimorfizm.

• W językach programowania ze

statyczną obsługą typów (C#) ważny
jest mechanizm dziedziczenia
(hierarchia).

background image

OCP na koniec

• Stosowanie programowania

obiektowego, bezmyślne wykorzystanie
abstrakcji we wszelkich możliwych
miejscach to nie jest zasada OCP.

• Najlepszym wyjściem jest definiowanie

abstrakcji tylko w tych obszarach
programowania, co do których istnieje
przypuszczenie szczególnie częstych
zmian.

background image

Problem testowy

• CA14
• Lsp+Ocp.doc

background image

Liskov Substitution

Principle

Treść: [W hierarchii dziedziczenia] podtypy muszą

być „podstawialne” za typy bazowe.

Opis: Zasada ta umożliwia wykrycie źle stworzonej

hierarchii dziedziczenia, w szczególności źle

zaimplementowaną klasę pochodną. Chodzi o to,

żeby wszystkie podtypy zachowywały się tak jak

zakłada się, że zachowuje się typ bazowy.

Zazwyczaj domyślnie się zakłada, że za typ bazowy

można podstawić klasę pochodną i zachowanie

będzie zmodyfikowane, ale będzie poprawne z

punktu widzenia „klientów” klasy bazowej.

Zasada ta jest nawet silniejsza niż podstawowa

zasada dziedziczenia „is-a” (jest).

background image

Liskov Substitution

Principle

• Zasada Liskov Substitution Principle (LSP)

wprowadza wskazówki dotyczące projektowania

używając dziedziczenia. Została sformułaowana

przez Barbarę Liskov i w wolnym tłumaczeniu

brzmi następująco:

Jeżeli dla każdego obiektu o1 typu S istnieje

obiekt o2 typu T, taki że dla wszystkich

programów P zdefiniowanych używając T,

zachowanie P pozostaje niezmienne po zamianie

o1 na o2 to typ S jest podtypem typu T.

background image

Przecież kwadrat jest (IS-A)

prostokątem

public

class

Rectangle

{

private

double

width;

private

double

height;

public virtual double

Width

{

get

{

return

width; }

set

{ width = value;}

}

public

virtual

double

Height

{

get

{

return

height; }

set

{ height = value;}

}

}

public class Square: Rectangle
{
public override double Width
{
set
{
base.Width = value;
base.Height = value;
}
}
public override double Height
{
set
{
base.Height = value;
base.Width = value;
}
}
}
public class RectangleTests
{
public void AreaOfRectangle()
{
Rectangle r = new Rectangle();//wstawmy Square

r.Width = 5;
r.Height = 2;
if (r.Area() != 10) throw new Exception("ZŁe pole");
}
}

background image

Relacja IS-A

• LSP mówi, że ta relacja musi

zachowywać zachowania

• Zachowanie prostokąta: w czasie

zmiany jednego z boków, drugi nie
może być zmieniony

• Zachowanie kwadratu: gdy

zmieniamy jeden bok to drugi też

background image

DBC

• Sposób projektowania

oprogramowania zaproponowany
przez Bertranda Meyera w latach 80

• Wspierany przez język Eiffel
• Idea:
• Kontrakt między klientem (miejsce

wywołania procedury) i dostawcą
(procedurą)

background image

Projektowanie przez

kontrakt

• Warunki wstępne
• Warunki końcowe
• Przestrzeganie kontraktu - asercje
• Dla prostokąta i kwadratu
• WW - puste
• Width=w;
• WK : width==w &&

height==old.height

background image

Proste i odcinki

public class Prosta
{
private Punkt p1;
private Punkt p2;

public Prosta(Punkt p1, Punkt p2)

{this.p1=p1; this.p2=p2;}

public Punkt P1 { get { return p1; } }
public Punkt P2 { get { return p2; } }
public double Slope { get {/* kod */} }
public double YIntercept { get {/* kod

*/} }

public virtual bool JestNa(Punkt p) {/*

kod */}

}

public class Odcinek : Prosta
{
public Odcinek(Punkt p1, Punkt

p2) : base(p1, p2) {}

public double Length() {

get {/* kod */} }

public override bool

JestNa(Punkt p) {/* kod */}

}

background image

I co teraz – naruszenie LSP

• Przymknąć oko. W końcu czy musimy

używać klasy bazowej w miejsce
pochodnej?

• Ale to świadczy o niezbyt wysokiej

jakości naszego kodu. Trzeba
przemyśleć.

• Wyodrębnić (factor) !

background image

To jest to

public abstract class ObiektLiniowy
{
private Punkt p1;
private Punkt p2;

public ObiektLiniowy(Punkt p1, Punkt p2)
{this.p1=p1; this.p2=p2;}

public Punkt P1 { get { return p1; } }
public Punkt P2 { get { return p2; } }

public double Slope { get {/* kod */} }
public double YIntercept { get {/* kod

*/} }

public virtual bool IsOn(Punkt p) {/* kod

*/}

}

public class Prosta : ObiektLiniowy
{
public Prosta(Punkt p1, Punkt p2) :

base(p1, p2) {}

public override bool IsOn(Punkt p) {/* kod

*/}

}
public class Odcinek : ObiektLiniowy
{
public Odcinek(Punkt p1, Punkt p2) :

base(p1, p2) {}

public double GetLength(){/* kod */}
public override bool IsOn(Punkt p) {/* kod

*/}

}
//Promień

background image

Klasyczne naruszenie LSP

• class Bazowa
• {

public virtual void f() {/*jakiś kod*/}

• }
• class Pochodna
• {
• public override void f(){}
• }

background image

OCP a LSP

• OCP jest jedną z podstawowych reguł

programowania obiektowego

• LSP jest jednym z warunków OCP
• Czynnikiem który naprawdę definiuje

podtyp, jest możliwość zastępowania
wynikająca z jawnego bądź
niejawnego kontraktu.

background image

Dependency-Inversion

Principle

Treść: Moduły wysokopoziomowe nie powinny zależeć

od modułów niskopoziomowych. Oba powinny zależeć

od abstrakcji. Abstrakcje nie powinny zależeć od

detali. Detale powinny zależeć od abstrakcji.

Opis: Główną ideą tej zasady jest wymuszenie, aby

moduły czy klasy nie zależały od bardziej

niskopoziomowych elementów, a raczej, aby

niskopoziomowe klasy czy moduły bazowały na

klasach abstrakcyjnych, czy wyższych modułach.

Dzięki temu niskopoziomowe zmiany nie będą miały

wpływu na wyższy poziom aplikacji. Zastosowanie tej

zasady najważniejsze jest przy podziale aplikacji na

warstwy, ale także powinno się ją stosować przy

projektowaniu klas.

background image

Dependency-Inversion

Principle

• Tam gdzie to tylko możliwe bądź zależny

od abstrakcji a nie konkretów. Czyli jeśli
chcesz mieć w klasie pole typu lista, to nie
deklaruj LinkedList, ArrayList ani nic
takiego, tylko po prostu List. To ułatwi ci
refaktoryzację jeśli okaże się, że trzeba
skorzystać z innej implementacji.

• Typowym naruszeniem DIP jest pobieranie

z formy danych (grid) i obliczanie np. sumy
w tej samej funkcji

background image

Dependency-Inversion

Principle

class B

{

public void ZrobCos() { }

}

class A

{

public void Metoda()

{

B b = new B();

b.ZrobCos();

}

}

Jakakolwiek zmiana w klasie B może mieć wpływ na zachowanie klasy A. Poza tym, klasa A może tylko sterować

zachowaniem B. Jak to zmienić?

interface IB

{

void ZrobCos() { }

}

class B : IB

{

public void ZrobCos() { }

}

class A

{

public void Metoda()

{

IB b = new B();

b.ZrobCos();

}

}

Co osiągnęliśmy?

-klasa A może sterować dowolnymi klasami implementującymi interface IB

background image

Dependency-Inversion

Principle

background image

Interface Segregation

Principle

Treść: klient nie powinien być zmuszony do

zależności od metod, których nie używa.

Opis: jest to chyba najrzadziej łamana zasada

ze wszystkich dotychczas przedstawionych.

Postuluje ona, aby interfejsy były jak

najbardziej skupione na jednym celu, a tym

samym nie były „przeładowane” metodami.

Jeżeli klasa implementuje interfejs, który ma

bardzo wiele metod to możliwe jest, że nie

wszystkie będzie wykorzystywać, a co gorsze

będzie musiała, co spowoduje złamanie SRP.

background image

Interface Segregation

Principle -naruszenie

• Najprostszym złamaniem ISP byłoby

umieszczenie w jednym interfejsie metod

służących do operacji graficznych (np.:

Draw(), Rotate()) oraz odpowiedzialnych za

trwałość (np.: Save(), Load()).

• Każdorazowo, gdy implementujemy

metodę interfejsu (bo musimy) i

zostawiamy ją pustą to oznacza

nadmiernie rozbudowany interfejs.

• Interface pollution

background image

Document Outline


Wyszukiwarka

Podobne podstrony:
JavaScript Zasady programowania obiektowego
JavaScript Zasady programowania obiektowego 2
JavaScript Zasady programowania obiektowego
JavaScript Zasady programowania obiektowego jascpo
Programowanie obiektowe(ćw) 1
Zadanie projekt przychodnia lekarska, Programowanie obiektowe
Programowanie obiektowe w PHP4 i PHP5 11 2005
Programowanie Obiektowe ZadTest Nieznany
Egzamin Programowanie Obiektowe Głowacki, Programowanie Obiektowe
Jezyk C Efektywne programowanie obiektowe cpefpo
Programowanie Obiektowe Ćwiczenia 5
Programowanie obiektowe(cw) 2 i Nieznany
programowanie obiektowe 05, c c++, c#
Intuicyjne podstawy programowania obiektowego0
Programowanie obiektowe, CPP program, 1
wyklad5.cpp, JAVA jest językiem programowania obiektowego
projekt01, wisisz, wydzial informatyki, studia zaoczne inzynierskie, programowanie obiektowe, projek
przeciazanie metod i operatorow, Programowanie obiektowe
projekt06, wisisz, wydzial informatyki, studia zaoczne inzynierskie, programowanie obiektowe, projek

więcej podobnych podstron