Witaj na Zine.net online Zaloguj się | Rejestracja | Pomoc

Zasada otwarty-zamknięty

Zasadę otwarty-zamknięty (ang. Open-Closed Principle, OCP) po raz pierwszy sformułował Bertrand Meyer [1]. Mówi ona, ze:

Klasa powinna być otwarta na rozszerzanie, ale zamknięta na modyfikacje.

Na pierwszy rzut oka powyższa zasada wydaje się być sama w sobie sprzeczna. Czy rzeczywiście możliwe jest, aby klasa była jednocześnie otwarta na rozszerzanie i zamknięta na modyfikacje? Czy rozszerzanie nie wiąże się z modyfikacją klasy? Otóż w kontekście zasady otwarty-zamknięty rozszerzanie rozumiane jest jako dodawanie nowej funkcji bez modyfikacji tej już istniejącej.

Przypuśćmy, że pracujemy nad programem do zarządzania budżetem domowym. Chcemy wykonywać rożne operacje, takie jak wpływy, wydatki i przelewy. Zdefiniowaliśmy sobie klasę Operation reprezentującą operację. Jej implementacja znajduje sie w przykładzie 1.

Przykład [C#] 1. Klasa reprezentująca operację.
public class Operation
{
    private OperationType type;
    public OperationType Type
    {
        get { return this.type; }
    }
   
    private decimal amount;
    public decimal Amount
    {
        get { return this.amount; }
        set { this.amount = value; }
    }
   
    public Operation (OperationType type)
    {
        this.type = type;
    }
}


W innej części programu definiujemy metodę, której zadaniem jest przetwarzanie operacji. Jej realizacje przedstawia przykład 2.

Przykład [C#] 2. Metoda przetwarzająca operacje
public void Execute ( Operation[] operationList )
{
    for ( int i = 0; i < operationList.Length; i++)
    {
        Operation operation = operationList[i];
        if ( operation.Type == OperationType.Income )
        {
            ProcessIncome( operation );
        }
        else if ( operation.Type == OperationType.Outcome )
        {
            ProcessOutcome ( operation );
        }
        else if ( operation.Type == OperationType.Transfer )
        {
            ProcessTransfer( operation );
        }
    }
}


Działanie metody z przykładu 2 jest proste. Iteruje ona po liście dostarczonych do przetworzenia operacji i bazując na typie danej operacji deleguje wykonanie operacji do odpowiedniej metody pomocniczej. Zastanówmy sie teraz dlaczego powyższa realizacja nie spełnia zasady otwarty-zamkniety. Według tej zasady klasa powinna być otwarta na rozszerzanie i zamknięta na modyfikacje. Spróbujmy zobaczyć jak wyglądałby proces rozszerzania tej realizacji o nowy typ operacji. Podstawową modyfikacją jaką musielibyśmy wprowadzić jest dodanie dodatkowej klauzuli else if w metodzie Execute tak, aby obsługiwać także nowy typ operacji. To jest właśnie przyczyna niezgodności z zasadą otwarty-zamknięty. Dodając nową funkcję w postaci nowego typu operacji musimy zmienić metodę Execute. Zmuszeni będziemy zmodyfikować istniejący już kod. Problemem, z jakim się tutaj spotykamy, jest uzaleznienie realizacji metody Execute od pewnego zbioru elementów. Jak możemy ten problem rozwiązać? Musimy zwolnić metodę Execute z konieczności decydowania co trzeba zrobić z daną operacją. Rozwiązaniem jest przeniesienie tej odpowiedzialności do samej operacji oraz ujednolicenie dostępu do niej. Możemy tego dokonać wykorzystując delegację. Definiując interfejs operacji wyszczególnimy metodę służąca do jej wykonania. Wtedy metoda Execute będzie mogła oddelegować wykonanie operacji do samej operacji. Przykład 3 zawiera definicje interfejsu operacji.

Przykład [C#] 3. Interfejs operacji.
public interface IOperation
{
    void Calculate();
}


Dzięki niemu realizując metodę Execute możemy abstrahować od typu operacji. Ulepszona jej realizacja znajduje sie w przykładzie 4.

Przykład [C#] 4. Ulepszona realizacja metody Execute.
public void Execute( IOperation[ ] operationList )
{
    for ( int i = 0; i < operationList.Length; i++ )
    {
        IOperation operation = operationList[i];
        operation.Calculate();
    }
}


W ulepszonym projekcie każda operacja musi być implementacją interfejsu IOperation. Zatem musimy zdefiniować trzy nowe klasy, które implementują interfejs IOperation i w metodzie Calculate wykonują operacje, które do tej pory wykonywane były przez odpowiednie metody pomocnicze wywoływane przez metodę Execute z przykładu 3.2.

Dlaczego takie rozwiązanie jest lepsze i zgodne z zasadą otwarty-zamknięty? Zastanówmy się ponownie nad tym, co należy zrobić, aby dodać nową operację. Ulepszona realizacja wymaga jedynie zdefiniowania nowej klasy implementującej interfejs IOperation. Taka klasa będzie już w pełni obsługiwana przez metodę Execute. Zauważmy, że nie modyfikujemy przy tym żadnego istniejącego do tej pory kodu. Jedynie dodajemy nową klasę. Zatem taki projekt jest w pełni zgodny z zasadą otwarty-zamknięty.

Zasada otwarty-zamknięty określa cechy charakterystyczne dobrego projektu. Dobry projekt to taki, w którym dodając nową funkcję nie musimy zmieniać istniejącego kodu. Wystarczy jedynie dodanie nowych podklas oraz przeciążanie metod. Poza tym nie modyfikując istniejącego kodu nie możemy niczego zepsuć. To jest bardzo ważna cecha z punktu widzenia pielęgnacji systemu.

[1] Meyer B.: Programowanie zorientowane obiektowo. Gliwice. Helion, 2005.
Opublikowane 2 września 2007 10:37 przez nuwanda

Komentarze:

# re: Zasada otwarty-zamknięty

17 kwietnia 2008 13:12 by belina

jestem zszokowany (pozytywnie), że oprócz mnie istnieje programista który zdaje sobie sprawę że ta zasada jest ważna i dobra....

pozdrawiam.

# re: Zasada otwarty-zamknięty

17 kwietnia 2008 13:23 by nuwanda

Trzeba popularyzować zasady projektowe! Uczyłem się tego na studiach i widzę, że mało się jeszcze o tym pisze. Na moim blogu znajdziesz jeszcze inne zasady i wzorce projektowe. Zapraszam do czytania i komentowania ;)

Pozdrawiam!

# M-V-P Twoim przyjacielem

12 października 2008 15:52 by nblog

Będąc na studiach zacząłem pracę programisty. Głowę miałem wypakowaną teorią, a w duszy grała chęć zastosowania

# re: Zasada otwarty-zamknięty

16 stycznia 2010 13:43 by Piotr Gabryanczyk

Pozwolilem sobie dodac do polskiej wikipedii nowe haslo:

http://pl.wikipedia.org/wiki/Zasada_Otwarty/Zamknięty

oraz nowa kategorie:

http://pl.wikipedia.org/wiki/Kategoria:Dobre_praktyki_programistyczne

Wydaje mi sie ze warto zebysmy zabrali sie w koncu za edukacje rzesz

mlodych programistow, dla ktorych dostep do angielskich zrodel nie

zawsze jest latwy.

Zachecam do dodawania kolejnych artykulow np. do przetlumaczenia jest

caly SOLID na poczatek.

Pewnie w ksiazkach takich jak "Clean Code", "Practices of Agile

Developer", "Emergent Design", czy "Beautiful Code" jest wiecej zasad

o ktorych polskojezyczny programista nie ma szans uslyszec.

# re: Zasada otwarty-zamknięty

3 stycznia 2014 05:48 by Mouddakir

bigos /     Żałuję, że nie dosatłem się na odpowiadający mi kiuenerk, bo z tego co wiem projektanci zarabiają krocie. Jest to ciężka praca, ale praca się zwraca.. zarobki duże. Z braku laku wybrałem informatykę i nie jest to wcale tak dobrze płatny zawf3d jak mf3wią..

Komentarze anonimowe wyłączone