Rozdział 16. Dziedziczenie

> Dodaj do ulubionych

Dziedziczenie to kolejny kreatywny spos├│b na realizacj─Ö lenistwa. Technika ta umo┼╝liwia wybieranie zachowa┼ä z klas i tworzenie na ich podstawie nowych o zmodyfikowanej funkcjonalno┼Ťci. W tym aspekcie dziedziczenie mo┼╝na traktowa─ç jako mechanizm tzw. wielokrotnego wykorzystania kodu. Mo┼╝na je te┼╝ wykorzystywa─ç na etapie projektowania programu, kt├│ry sk┼éada si─Ö z pewnej grupy powi─ůzanych ze sob─ů obiekt├│w.

Dziedziczenie umo┼╝liwia na wybieranie do klasy zachowa┼ä z klasy, kt├│ra jest jej rodzicem. Implementacj─Ö interfejsu przez klas─Ö mo┼╝na traktowa─ç jako jej o┼Ťwiadczenie, ┼╝e ma okre┼Ťlony zestaw zachowa┼ä. Je┼Ťli klasa jest potomkiem okre┼Ťlonej klasy nadrz─Ödnej, to znaczy, ┼╝e ma zbi├│r zachowa┼ä, poniewa┼╝ odziedziczy┼éa je po swoim rodzicu. Kr├│tko m├│wi─ůc:

Interfejs: ÔÇ×Potrafi─Ö robi─ç te rzeczy, bo powiedzia┼éam, ┼╝e potrafi─ÖÔÇŁ.

Dziedziczenie: ÔÇ×Potrafi─Ö robi─ç te rzeczy, bo m├│j rodzic potrafiÔÇŁ.

Rozszerzanie klasy nadrz─Ödnej

Przyk┼éad wykorzystania dziedziczenia mo┼╝ecie zobaczy─ç w naszym projekcie konta bankowego. Zauwa┼╝yli┼Ťmy ju┼╝, ┼╝e konto BabyAccount musi zachowywa─ç si─Ö jak CustomerAccount, tylko powinno mie─ç zmodyfikowan─ů metod─Ö obs┼éuguj─ůc─ů wyp┼éacanie pieni─Ödzy. Posiadacze zwyk┼éych kont mog─ů wyp┼éaca─ç dowolne kwoty. Posiadacze kont dzieci─Öcych mog─ů pobiera─ç maksymalnie 50 z┼é na raz.

W kategoriach projektowych rozwi─ůzali┼Ťmy ten problem za pomoc─ů interfejs├│w. Oddzielaj─ůc wykonawc─Ö czynno┼Ťci od jej opisu (czyli tego, co reprezentuje interfejs), mo┼╝emy oprze─ç ca┼éy nasz system bankowy na interfejsie IAccount, a nast─Öpnie dora┼║nie dodawa─ç konta o innych zachowaniach. Mo┼╝emy nawet tworzy─ç ca┼ékiem nowe konta w dowolnym czasie po wdro┼╝eniu systemu. B─Öd─ů one prawid┼éowo dzia┼éa─ç z innymi, poniewa┼╝ b─Öd─ů si─Ö prawid┼éowo zachowywa─ç (tzn. b─Öd─ů implementowa─ç interfejs).

To jednak robi si─Ö nu┼╝─ůce podczas pisania programu. Musimy utworzy─ç klas─Ö BabyAccount, kt├│rej kod w znacznej mierze pokrywa si─Ö z kodem klasy CustomerAccount. Mo┼╝ecie pomy┼Ťle─ç, ┼╝e to ┼╝aden problem, bo przecie┼╝ wystarczy skopiowa─ç odpowiednie fragmenty w edytorze tekstu programu. Ale:

Z┼éota my┼Ťl programisty: Kopiowania fragment├│w kodu to z┼éo

Wci─ů┼╝ zdarza mi si─Ö pope┼énia─ç b┼é─Ödy podczas pisania program├│w. Pewnie my┼Ťlicie, ┼╝e po tylu latach w tej bran┼╝y powinienem wszystko robi─ç poprawnie za pierwszym razem. Jest odwrotnie. Na dodatek wiele z b┼é─Öd├│w, kt├│re pope┼éniam, wi─ů┼╝e si─Ö z nieprawid┼éowym kopiowaniem blok├│w kodu. Napisz─Ö jaki┼Ť kod i stwierdz─Ö, ┼╝e potrzebuj─Ö czego┼Ť podobnego, ale nie identycznego, w innym miejscu programu. Wtedy kopiuj─Ö blok kodu. Nast─Öpnie zmieniam wi─Ökszo┼Ť─ç, ale nie ca┼éo┼Ť─ç, nowego kodu i stwierdzam, ┼╝e m├│j program dzia┼éa nieprawid┼éowo.

Starajcie si─Ö tego unika─ç. Dobry programista ka┼╝dy fragment kodu pisze tylko raz. Je┼Ťli chcesz u┼╝y─ç czego┼Ť wi─Öcej ni┼╝ raz, zamie┼ä to w metod─Ö.

Powinni┼Ťmy wzi─ů─ç wszystkie zachowania z klasy CustomerAccount i zmieni─ç tylko t─Ö jedn─ů metod─Ö, kt├│ra ma dzia┼éa─ç inaczej. Okazuje si─Ö, ┼╝e w j─Özyku C# mo┼╝na co┼Ť takiego zrobi─ç dzi─Öki wykorzystaniu dziedziczenia. Przy tworzeniu klasy BabyAccount mog─Ö powiedzie─ç kompilatorowi, ┼╝e bazuje ona na klasie CustomerAccount:

public class BabyAccount : CustomerAccount,IAccount
{
}

Najwa┼╝niejsza tutaj jest wyr├│┼╝niona cz─Ö┼Ť─ç kodu za nazw─ů klasy. Umie┼Ťci┼éem tam nazw─Ö klasy, kt├│r─ů rozszerza klasa BabyAccount. To znaczy, ┼╝e klasa BabyAccount potrafi robi─ç wszystko to, co klasa CustomerAccount.

Teraz mog─Ö pisa─ç taki kod:

BabyAccount b = new BabyAccount(); b.PayInFunds(50);

To jest mo┼╝liwe, poniewa┼╝ cho─ç klasa BabyAccount nie zawiera metody PayInFunds, ma j─ů klasa nadrz─Ödna. To znaczy, ┼╝e w tym przypadku u┼╝ywana jest metoda PayInFunds z klasy CustomerAccount.

W takim razie obiekty klasy BabyAccount potrafi─ů robi─ç wszystko to, co obiekty jej klasy nadrz─Ödnej. W tej chwili klasa BabyAccount nie ma ┼╝adnych w┼éasnych zachowa┼ä, tylko same odziedziczone po rodzicu.

Przesłanianie metod

Wiemy ju┼╝, ┼╝e mo┼╝emy tworzy─ç nowe klasy na bazie istniej─ůcych. Teraz chcieliby┼Ťmy dowiedzie─ç si─Ö, jak zmieni─ç zachowanie interesuj─ůcej nas metody, a konkretnie jak wymieni─ç metod─Ö WithdrawFunds na now─ů. Ta czynno┼Ť─ç nazywa si─Ö przes┼éanianiem metody. W klasie BabyAccount mo┼╝emy to zrobi─ç nast─Öpuj─ůco:

public class BabyAccount : CustomerAccount,IAccount
{
public override bool WithdrawFunds (decimal amount)
{
if (amount > 10)
{
return false;
}
if (balance < amount)
{
return false;
}
balance = balance - amount; return true;
}
}

S┼éowo kluczowe override oznacza, ┼╝e wolimy u┼╝y─ç tej metody zamiast metody o takiej samej nazwie z klasy nadrz─Ödnej. W zwi─ůzku z tym w takim kodzie, jak ten:

BabyAccount b = new BabyAccount(); b.PayInFunds(50); b.WithdrawFunds(5);

wywołanie PayInFunds spowoduje użycie metody z klasy nadrzędnej (ponieważ ta nie została przesłonięta), natomiast wywołanie metody WithdrawFunds spowoduje użycie wersji z klasy BabyAccount.

Metody wirtualne

Aby przes┼éoni─Öcie zadzia┼éa┼éo, musimy zrobi─ç jeszcze jedn─ů rzecz. Kompilator C# musi wiedzie─ç, czy dana metoda b─Ödzie przes┼éaniana. Wynika to z tego, ┼╝e przes┼éoni─Öt─ů metod─Ö musi wywo┼éywa─ç w odrobin─Ö inny spos├│b ni┼╝ ÔÇ×normaln─ůÔÇŁ. Innymi s┼éowy, powy┼╝szy kod nie da si─Ö prawid┼éowo skompilowa─ç, poniewa┼╝ nie poinformowano kompilatora, ┼╝e metoda WithDrawFunds mo┼╝e zosta─ç przes┼éoni─Öta w klasach potomnych.

Aby dope┼éni─ç wszystkich wymog├│w formalnych przes┼éoni─Öcia, musz─Ö zmieni─ç moj─ů deklaracj─Ö metody w klasie CustomerAccount.

public class CustomerAccount : IAccount
{

private decimal balance = 0;

public virtual bool WithdrawFunds ( decimal amount )
{
if (balance < amount)
{
return false;
}
balance = balance - amount; return true;
}

}

S┼éowo kluczowe virtual oznacza ÔÇö ÔÇ×Mog─Ö potrzebowa─ç innej wersji tej metody w klasie potomnejÔÇŁ. Nie musicie tego robi─ç, ale dodatek tego s┼éowa kluczowego Wam to umo┼╝liwia.

To sprawia, ┼╝e s┼éowa kluczowe override i virtual s─ů czym┼Ť w rodzaju nieroz┼é─ůcznej pary. S┼éowo kluczowe virtual oznacza, ┼╝e dana metoda mo┼╝e by─ç przes┼éaniana, a s┼éowo kluczowe override s┼éu┼╝y do definiowania nowej wersji tej metody.

Ochrona danych w hierarchii klas

Okazuje si─Ö, ┼╝e powy┼╝szy kod wci─ů┼╝ jeszcze nie zadzia┼éa. Wi─ů┼╝e si─Ö to z tym, ┼╝e warto┼Ť─ç salda w klasie CustomerAccount jest prywatna. Tak j─ů zdefiniowali┼Ťmy, aby inne klasy nie mia┼éy do niej dost─Öpu i nie mog┼éy jej zmienia─ç.

Tylko ┼╝e to jest zbyt surowe ograniczenie, kt├│re uniemo┼╝liwia modyfikowanie tej warto┼Ťci klasie BabyAccount. Do rozwi─ůzania tego problemu w j─Özyku C# istnieje mniej restrykcyjny modyfikator dost─Öpu o nazwie protected. Opatrzone nim sk┼éadowe s─ů widoczne w klasach rozszerzaj─ůcych klas─Ö nadrz─Ödn─ů. Innymi s┼éowy metody w klasie BabyAccount ÔÇ×widz─ůÔÇŁ i mog─ů u┼╝ywa─ç chronionej sk┼éadowej, poniewa┼╝ znajduj─ů si─Ö w tej samej hierarchii klas, co klasa zawieraj─ůca t─Ö sk┼éadow─ů.

Hierarchia klas jest niczym drzewo rodzinne. Każda klasa ma rodzica i może robić wszystko to, co ten rodzic. Ponadto ma dostęp do wszystkich składowych chronionych swojej klasy nadrzędnej.

public class CustomerAccount : IAccount
{
protected decimal balance = 0;

.....
}

Niech─Ötnie to robi─Ö, poniewa┼╝ balance jest niezwykle wa┼╝n─ů warto┼Ťci─ů i wola┼ébym, ┼╝eby nikt spoza klasy CustomerAccount jej nie u┼╝ywa┼é. Na razie jednakta zmiana umo┼╝liwi dzia┼éanie programu. P├│┼║niej poka┼╝─Ö Wam lepsze sposoby na za┼éatwienie tej sprawy.

IAccount[] accounts = new IAccount[MAX_CUST];

accounts[0] = new CustomerAccount(); accounts[0].PayInFunds(50);
Console.WriteLine("Saldo: " + accounts[0].GetBalance());

accounts[1] = new BabyAccount(); accounts[1].PayInFunds(20);
Console.WriteLine("Saldo: " + accounts[1].GetBalance());

if (accounts[0].WithdrawFunds(20))
{
Console.WriteLine("Wypłata OK");
}
if (accounts[1].WithdrawFunds(20))
{
Console.WriteLine("Wypłata OK");
}

Przykład kodu 39. Wykorzystanie dziedziczenia

Powy┼╝szy kod ilustruje sposoby tworzenia i u┼╝ycia r├│┼╝nych typ├│w kont ÔÇö klasa BabyAccount zosta┼éa utworzona na bazie nadrz─Ödnego typu CustomerAccount. Je┼Ťli masz wra┼╝enie deja vu, to pewnie dlatego, ┼╝e to jest dok┼éadnie taki sam kod, jak w poprzednim przyk┼éadzie. Kod wykorzystuj─ůcy te obiekty b─Ödzie dzia┼éa┼é dok┼éadnie tak samo, jak zawsze, natomiast same obiekty b─Öd─ů zachowywa┼éy si─Ö odrobin─Ö inaczej, dzi─Öki czemu program stanie si─Ö mniejszy (co nas za bardzo nie martwi) i ┼éatwiejszy do debugowania (poniewa┼╝ jakikolwiek b┼é─ůd w kt├│rej┼Ť ze wsp├│lnych metod wymaga naprawy tylko w jednym miejscu).

Zapiski bankiera: Przes┼éanianie dla po┼╝ytku i przyjemno┼Ťci

Mo┼╝liwo┼Ť─ç przes┼éaniania metod to bardzo cenny element funkcjonalno┼Ťci j─Özyka programowania. Dzi─Öki niemu mo┼╝emy tworzy─ç bardziej og├│lne klasy (takie jak CustomerAccount) i nast─Öpnie dostosowywa─ç je do specyficznych potrzeb (na przyk┼éad BabyAccount). Oczywi┼Ťcie takie dzia┼éania powinny by─ç zaplanowane ju┼╝ na etapie projektowania programu. A to oznacza, ┼╝e powinni┼Ťcie zgromadzi─ç wi─Öcej metadanych od klienta, aby zdecydowa─ç, kt├│re elementy funkcjonalno┼Ťci b─Öd─ů wymaga┼éy modyfikacji w p├│┼║niejszym czasie. Metod─Ö WithDrawFunds uczyniliby┼Ťmy wirtualn─ů, poniewa┼╝ kierownik powiedzia┼éby nam, ┼╝e chce mie─ç mo┼╝liwo┼Ť─ç r├│┼╝nicowania sposob├│w wyp┼éacania pieni─Ödzy. A my zapisaliby┼Ťmy to w specyfikacji.

Metoda bazowa

Pami─Ötajcie, ┼╝e programi┼Ťci s─ů leniwi z zasady i zawsze szukaj─ů sposobu, aby tylko raz napisa─ç kod rozwi─ůzuj─ůcy dany problem. W takim razie wygl─ůda na to, ┼╝e ┼éamiemy w┼éasne zasady, poniewa┼╝ metoda WithDrawFunds klasy BabyAccount zawiera ca┼éy kod metody z klasy nadrz─Ödnej.

Ju┼╝ pisa┼éem, ┼╝e mi si─Ö to nie podoba, poniewa┼╝ zmusza nas do udzielenia wi─Ökszych uprawnie┼ä dost─Öpu do warto┼Ťci balance ni┼╝ by┼Ťmy chcieli. Na szcz─Ö┼Ťcie projektanci j─Özyka C# pomy┼Ťleli o tym i stworzyli mechanizm umo┼╝liwiaj─ůcy wywo┼éanie metody bazowej w metodzie, kt├│ra j─ů przes┼éania.

W tym przypadku s┼éowo ÔÇ×bazowaÔÇŁ oznacza odwo┼éanie do tego, co zosta┼éo przes┼éoni─Öte. Z pomoc─ů tego mechanizmu mog─Ö znacznie upro┼Ťci─ç metod─Ö WithDrawFunds w mojej klasie BabyAccount:

public class BabyAccount : CustomerAccount, IAccount
{
public override bool WithdrawFunds (decimal amount)
{
if (amount > 10)
{
return false;
}
return base.WithdrawFunds(amount);
}
}

Przykład kodu 40. Przesłonięcie metody bazowej

W ostatnim wierszu metody WithDrawFunds znajduje si─Ö wywo┼éanie jej pierwowzoru ÔÇö metody WithDrawFunds z klasy nadrz─Ödnej, tzn. tej, kt├│r─ů ta metoda przes┼éania. Koniecznie musicie zrozumie─ç, co i dlaczego ja tu robi─Ö:

  • Nie chc─Ö pisa─ç dwa razy tego samego kodu
  • Nie chc─Ö, aby warto┼Ť─ç balance by┼éa widoczna na zewn─ůtrz klasy CustomerAccount.

U┼╝ycie s┼éowa kluczowego base do wywo┼éania przes┼éoni─Ötej metody rozwi─ůzuje oba te problemy w bardzo elegancki spos├│b. Jako ┼╝e ta metoda zwraca w wyniku warto┼Ť─ç bool, mog─Ö wys┼éa─ç cokolwiek od niej dostan─Ö. Dzi─Öki tej zmianie mog─Ö z powrotem uczyni─ç warto┼Ť─ç balance prywatn─ů w klasie CustomerAccount, poniewa┼╝ nie jest ona modyfikowana na zewn─ůtrz.

Warto zwr├│ci─ç uwag─Ö na jeszcze inne konsekwencje wprowadzonych tu zmian. Je┼Ťli b─Öd─Ö chcia┼é naprawi─ç b┼é─ůd w zachowaniu metody WithDrawFunds, to wystarczy zrobi─ç to tylko raz, w klasie najwy┼╝szego poziomu, a poprawka b─Ödzie obecna we wszystkich klasach, kt├│re z niej korzystaj─ů.

Zamiana metody

To mo┼╝e wydawa─ç si─Ö dziwne, ale je┼Ťli dobrze si─Ö zastanowisz, to zrozumiesz, ┼╝e ma sens. Kiedy pobawisz si─Ö troch─Ö z j─Özykiem C#, to odkryjesz, ┼╝e tak naprawd─Ö wcale nie potrzebujesz s┼éowa kluczowego virtual, aby przes┼éoni─ç metod─Ö. Je┼Ťli je opuszcz─Ö (i to samo zrobi─Ö ze s┼éowem override), program nadal b─Ödzie dobrze dzia┼éa┼é.

To wynika z tego, ┼╝e w tej sytuacji nie odbywa si─Ö ┼╝adne przes┼éoni─Öcie. Po prostu stworzyli┼Ťmy now─ů wersj─Ö metody (kompilator C# nawet powiadomi Was, ┼╝e powinni┼Ťcie to zaznaczy─ç za pomoc─ů s┼éowa kluczowego new):

public class BabyAccount : CustomerAccount,IAccount
{
public new bool WithdrawFunds (decimal amount)
{
if (amount > 10)
{
return false;
}
if (balance < amount)
{
return false;
}
balance = balance - amount; return true;
}
}

Z┼éota my┼Ťl programisty: Nie zamieniaj metod

Jestem zdecydowanie przeciwny zamianie metod zamiast ich przes┼éaniania. Je┼Ťli zamierzasz pozwoli─ç programistom tworzy─ç specjalne wersje klas w taki spos├│b, to o wiele lepszym rozwi─ůzaniem jest stosowanie techniki przes┼éaniania, kt├│ra u┼éatwia zapanowanie nad przes┼éoni─Ötym kodem. W og├│le zastanawiam si─Ö, po co ja o tym pisa┼éem.

Uniemożliwianie przesłaniania

Przes┼éanianie to technika daj─ůca du┼╝e mo┼╝liwo┼Ťci. Dzi─Öki niej programista mo┼╝e stworzy─ç now─ů klas─Ö zawieraj─ůc─ů wszystkie zachowania klasy nadrz─Ödnej i odpowiednio je zmodyfikowa─ç. To pozwala na stosowanie metody projektowania, w my┼Ťl kt├│rej im klasa jest ni┼╝ej w hierarchii ÔÇ×drzewa rodzinnegoÔÇŁ, tym jest bardziej specyficzna.

Tylko ┼╝e przes┼éanianie/zamiana nie zawsze s─ů po┼╝─ůdane. We┼║my na przyk┼éad metod─Ö GetBalance. Jej nigdy nie b─Ödzie trzeba wymienia─ç. A mimo to jaki┼Ť niesubordynowany programista mo┼╝e napisa─ç w┼éasn─ů i przes┼éoni─ç lub zamieni─ç ni─ů metod─Ö z klasy nadrz─Ödnej:

public new decimal GetBalance ()
{
return 1000000;
}

To bankowy odpowiednik butelki piwa, która nigdy nie robi się pusta. Niezależnie od tego, ile gotówki pobierzemy, ta metoda zawsze będzie pokazywać, że na koncie jest milion złotych!

Ten przebieg┼éy programista mo┼╝e umie┼Ťci─ç t─Ö metod─Ö w klasie, aby da─ç sobie mo┼╝liwo┼Ť─ç robienia nieograniczonych zakup├│w. To znaczy, ┼╝e musimy mie─ç mo┼╝liwo┼Ť─ç oznaczania niekt├│rych metod jako ÔÇ×nieprzes┼éanialnych. W j─Özyku C# s┼éu┼╝y do tego s┼éowo kluczowe sealed, kt├│re oznacza ÔÇô ÔÇ×Tej metody nie mo┼╝na przes┼éania─çÔÇŁ.

Niestety, aby skorzysta─ç z tej opcji, trzeba si─Ö troch─Ö nam─Öczy─ç. Zasady s─ů takie, ┼╝e zapiecz─Ötowa─ç (and. seal) mo┼╝na tylko metod─Ö przes┼éaniaj─ůc─ů (czyli nie mo┼╝emy tego zrobi─ç z metod─ů wirtualn─ů GetBalance w klasie CustomerAccount), a poza tym jaka┼Ť wredna osoba zawsze mo┼╝e zamieni─ç zapiecz─Ötowan─ů metod─Ö w klasie nadrz─Ödnej na niezapiecz─Ötowan─ů metod─Ö o takiej samej nazwie w klasie potomnej.

Innym zastosowaniem słowa kluczowego sealed, które ma trochę większy potencjał praktyczny, jest uniemożliwienie rozszerzania klasy, tzn. sprawienie, że nie będzie mogła być używana jako baza do utworzenia innej klasy.

public sealed class BabyAccount : CustomerAccount,IAccount
{
.....
}

Teraz kompilator nie pozwoli na u┼╝ycie klasy BabyAccount jako bazy do utworzenia kolejnego typu konta.

Zapiski bankiera: Chroń swój kod

Je┼Ťli chodzi o aplikacj─Ö bankow─ů, klient niespecjalnie b─Ödzie si─Ö przejmowa┼é tym, w jaki spos├│b u┼╝ywasz s┼éowa kluczowego sealed w swoich programach. Jego b─Ödzie interesowa┼éo to, czy Tw├│j kod dzia┼éa prawid┼éowo. Pracuj─ůc w tej bran┼╝y musicie pogodzi─ç si─Ö z faktem, ┼╝e nie wszyscy u┼╝ytkownicy waszych komponent├│w b─Öd─ů mili i przyja┼║nie nastawieni. W zwi─ůzku z tym podczas projektowania programu zawsze dobrze si─Ö zastan├│wcie, czy dane metody powinny by─ç wirtualne oraz zawsze stosujcie piecz─Ötowanie, je┼Ťli jest taka mo┼╝liwo┼Ť─ç.

Na tym poziomie nauki programowania chyba nie ma sensu rozwodzi─ç si─Ö nad zawi┼éo┼Ťciami tego typu, wi─Öc nie przejmujcie si─Ö, je┼Ťli nie wszystko to do was trafia. Wystarczy zapami─Öta─ç, ┼╝e podczas tworzenia programu jest jeszcze jeden czynnik ryzyka, kt├│ry trzeba wzi─ů─ç pod uwag─Ö.

Konstruktory i hierarchie

Konstruktor jest metod─ů, kt├│ra kontroluje proces tworzenia obiektu. Z jej pomoc─ů programista mo┼╝e ustawi─ç pocz─ůtkowe warto┼Ťci w obiekcie:

robsAccount = new BabyAccount("Rob Miles", 100);

W tym przypadku chcemy utworzy─ç dla dziecka o nazwisku Rob Miles nowe konto z saldem pocz─ůtkowym 100 z┼é.

Ten kod zadzia┼éa tylko, je┼Ťli klasa BabyAccount b─Ödzie mia┼éa konstruktor przyjmuj─ůcy ┼éa┼äcuch jako pierwszy parametr i warto┼Ť─ç dziesi─Ötn─ů jako drugi.

Pewnie my┼Ťlicie sobie, ┼╝e m├│g┼ébym napisa─ç taki konstruktor:

public BabyAccount (string inName, decimal inBalance)
{
name = inName; balance = inBalance;
}

Tylko że klasa BabyAccount jest rozszerzeniem klasy CustomerAccount. Innymi słowy, aby utworzyć obiekt klasy BabyAccount, program musi utworzyć obiekt klasy CustomerAccount.

W takim razie powinni┼Ťmy zacz─ů─ç od dodania konstruktora do klasy CustomerAccount, kt├│ra ustawia imi─Ö i nazwisko oraz saldo konta klienta:

public CustomerAccount (string inName, decimal inBalance) :
base ( inName, inBalance)
{
// ustawianie imienia i nazwiska oraz salda
}

Maj─ůc konstruktor w klasie nadrz─Ödnej, mo┼╝emy napisa─ç konstruktor w klasie potomnej, a nast─Öpnie mo┼╝emy go wywo┼éa─ç. Do wywo┼éywania konstruktora klasy nadrz─Ödnej s┼éu┼╝y s┼éowo kluczowe base.

S┼éowa kluczowego base u┼╝ywa si─Ö tak samo, jak this do wywo┼éywania innego konstruktora tej samej klasy. Powy┼╝szy konstruktor zak┼éada, ┼╝e klasa Account, dla kt├│rej klasa CustomerAccount jest potomkiem, zawiera konstruktor przyjmuj─ůcy dwa parametry ÔÇö pierwszy jest ┼éa┼äcuchem, a drugi jest warto┼Ťci─ů dziesi─Ötn─ů.

Łańcuchy konstruktorów

Przy projektowaniu konstruktor├│w i hierarchii klas nale┼╝y pami─Öta─ç, ┼╝e do utworzenia obiektu klasy potomnej potrzebne jest uprzednie utworzenie egzemplarza klasy nadrz─Ödnej. To znaczy, ┼╝e konstruktor z klasy nadrz─Ödnej musi zosta─ç wywo┼éany przed konstruktorem z klasy potomnej. Innymi s┼éowy, aby utworzy─ç obiekt klasy BabyAccount, najpierw musisz utworzy─ç obiekt klasy CustomerAccount. To prowadzi do konieczno┼Ťci tworzenia tzw. ┼éa┼äcuch├│w konstruktor├│w. Programista musi dopilnowa─ç, aby na ka┼╝dym poziomie procesu tworzenia zosta┼é wywo┼éany konstruktor klasy nale┼╝─ůcej do tego poziomu.

To ilustruje problem, jaki mo┼╝ecie napotka─ç z hierarchiami klas. S─ů do┼Ť─ç delikatne i trzeba si─Ö z nimi obchodzi─ç jak z jajkiem. Zmiany w jednej cz─Ö┼Ťci hierarchii klas mog─ů poci─ůga─ç za sob─ů konieczno┼Ť─ç dokonania zmian w innych cz─Ö┼Ťciach. W powy┼╝szym przypadku wpadli┼Ťmy na sprytny pomys┼é, jak udoskonali─ç proces tworzenia obiekt├│w klasy BabyAccount, co sko┼äczy┼éo si─Ö konieczno┼Ťci─ů wprowadzenia zmian tak┼╝e w klasie CustomerAccount.

Z┼éota my┼Ťl programisty: Zaplanuj proces korukcji obiekt├│w swoich klas

Spos├│b tworzenia obiekt├│w swoich klas w systemie powinni┼Ťcie mie─ç zaplanowany z g├│ry. Jest to element og├│lnej architektury budowanego systemu. Czasami my┼Ťl─Ö sobie o tych rzeczach jak o filarach, na kt├│rych opieraj─ů si─Ö stropy i dach budynku. Informuj─ů one programist├│w odpowiedzialnych za implementacj─Ö o tym, jak tworzy─ç te komponenty. Oczywi┼Ťcie te projekty powinny by─ç spisane i dost─Öpne dla zespo┼éu programistycznego.

Autor: Rob Miles

Tłumaczenie: Łukasz Piwko