Наслеђивање (објектно-оријентисано програмирање)

Из Википедије, слободне енциклопедије

Наслеђивање је када објекат или класа базира на другом објекту (прототипско програмирање) или класи (класно-оријентисано програмирање), користећи исту имплементацију (наслеђену од објекта или класе), специфирајући имплементацију да се понаша исто (интерфејсинг; наслеђено понашање). То је механизам за поновно коришћење кода који омогућава независност екстензије од оригиналног софтвера помоћу класа и интерфејса. Однос објеката или класа током наслеђивања је допринео развоју хијерархије. Наслеђивање је осмишљено 1967, у Симули.[1]

Наслеђивање не би требало да буде конфузно са подтипом.[2][3] У неким језицима се наслеђивање и подтипови слажу,[а]док су у неким програмским језицима различити; генерално подтип успоставља је-a релацију, док наслеђивање само поново користи имплементацију и устоставља синтатичку равнотежу, не стално и семантичку (Наслеђивање не гарантује подтипско понашање). Да бисмо разликовали ове концепте, кажемо и да је сабтајпинг познат и као интерфејсно наслеђивање, док је наслеђивање описано као наслеђивање имплементација или наслеђивање кода.[4]  Наслеђивање је најчешће коришћен механизам за успостављање равнотеже подтипских релација.[5]

Наслеђивање чини објектна композиција, где један објекат садржи други објекат (или објекат једне класе садржи објекат друге класе), види композицију преко наслеђивања. Композиције имплементирају има-a равнотежу, што је супротно од равнотеже коју успоставља подтип (је-a).

Типови наслеђивања[уреди]

Једноструко наслеђивање
Вишесложно и вишеструко наслеђивање

Постоје различити типови наслеђивања,[6] базираних на парадигмалном и специфичном програмирању.

Једноструко наслеђивање
Када подкласа наслеђује особине једне супер класе. Класа добија особине друге класе.
Вишеструко наслеђивање
Када једна класа може да има више родитељских класа и наслеђује особине свих родитељских класа.
Вишесложно наслеђивање
 : када је подкласа нслеђена од друге подкласе. Није неуобичајено да је класа изведена од друге изведене класе као што и сам наслов говори "Вишесложно наслеђивање". Класа А је база за изведену класу B, док је класа B база за изведену класу C. За класу B се каже да је средња база-класа јер представља везу за наслеђивање између класе А и класе C. Ланац ABC се још назива и путања наслеђивања.

Изведена класа вишесложног наслеђивања:

Class A(...); //Родитељска класа
Class B : public A(...); //B изведена из A
Class C : public B(...); //C изведена B

Овај процес се може проширити на вишебројни ниво.

Хијерархијско наслеђивање
када је једна класа-суперкласа (базна класа) за више подкласа.
Хибридно наслеђивање
од два или више типа наслеђивања.

Подкласе и суперкласе[уреди]

 Подкласа, изведена класа, дечија класа, је модуларна , дериватна класа која наслеђује једно или више језичких ентитета од једне или више других класа (суперкласа, базна класа, родитељска класа). Семантика наслеђивања класе варира од програма до програма, али углавном подкласе аутоматски наслеђују варијабле и чланове функција из суперкласе. Општи облик дефинисања изведене класе је:  

class derived-class-name : visibility-mode base-class-name
{
 .....//
 .....// чланови изведене класе
 .....//
};
  • Двотачка означава да је изведена класа име, изведена из базне класе име. Видљивост је опционална и може бити скривена или јавна. Произвољно изабрани мод је скривен. Видљивост карактерише базну класу скривено или јавно.

Неки програми подржавају наслеђивање и других конструкција. Нпр. у Ајфелу, „уговор“ који карактерише спецификацију класа, карактерише и наследнике. Суперкласа омогућава заједнички приступ и оснивачку функционалност, што доводи до тога да подкласе могу да наслеђују, модификују и суплементирају. Софтверско наслеђивање уз помоћ подкласа је поново коришћено у подкласама. Референца инстанце класе може се односити на неку од њених подкласа. Стварну класу овјеката која је референцована, тешко је предвидети у преведено време. Јединствени приступ се користи да се позову чланови функције броја објеката друих класа. Подкласа може да замени функције суперкласе са потпуно новим функцијама које морају да имају исту методу.

Ненаследне класе[уреди]

У неким програмима класа може бити означена као ненаследна, додавајући класне модификаторе у опис класе. Примери укључују финалну кључну реч у Јави или заглављену кључну реч у C#. Такви модификатори су додати у класној декларацији пре класне кључне речи и класног проналазача декларације. Тако заглављене класе имају ограничену употребљивост, када једино девелопери имају приступ реорганизованом бинарном фајлу или изворном коду.

Заглављена класа нема подлкасе, тако да може бити одбијена у сложеном времену, тако да су референце или показивачки објекта класе референцне инстанце класа али не и инстанце подкласа (које не постоје) или инстанце суперкласа. Подтипски полиморфизам. Зато што је тачан тип објекта био референцован пре извршења,рано везивање (статично обавештење) може бити коришћео уместо касног везивања (још познато и као „динамично обавештење), која подразумева једну или више виртуалних метода табела, чији изглед зависи од тога да ли програм подржава вишесложно наслеђивање или једносложно наслеђивање

Методе које не могу бити прескочене[уреди]

Класе могу бити затворене/завршене, декларација метода може да садржи модификатор метода који спречава да се метод прескочи (и.е. заменити новом функцијом са истим именом и типом у подкласи). Скривен метод је веома једноставан зато што није доступан другим класа осим класи која је члан функције (ово није тачно у C++). Финална метода у Јави, скртивена метода у C# или залеђена особина у Ајфелу не могу бити прескочене.

Виртуалне методе[уреди]

Ако је метода суперкласе виртуална метода, призивање суперкласних метода ће бити динамично послато. Неки програми подразумевају да методе буду специфично декларисано као виртуалне (нпр. C++) у неким програмима све методе су виртуалне (нпр. Јава). Позиви не-виртуалне методе ће бити увек статички послате (адреса позива функције се налази у сложеном-времену). Статично слање је брже од динамичког и садржи оптимизам који се назива стаза за проширење.

Видљивост наслеђених чланова[уреди]

Base class 

видљивост

Видљивост изведене класе
Јавна

деривација

Приватна

деривација

Осигурана

деривација

  • Скривена →
  • Осигурана →
  • Јавна →
  • Ненаслеђена
  • Осигурана
  • Јавна
  • Ненаслеђена
  • приватна
  • приватна
  • Ненаслеђена
  • Осигурана
  • Осигурана

[7]

Апликације[уреди]

Апликације се користе да две или више класе умреже.

  • Прелазак преко 

Многи објектно-оријентисани програмски језици допуштају класи или објекту да замене имплементацију једног аспекта-у главном понашање-које је наследио. Овај процес се често зове прелазак преко. Прелазак преко захтева компилацију: која верзија понашања има инстанцу наслеђене класе-део те дате класе или део из родитељске (базне) класе? Одговор варира у зависности од програмског језика, док су неки језици развили способност да покажу да партикуларно понашање неће бити пређено и да треба да се понаша као што је дефинисано од стране родитељске класе. На пример, у C#, базне методе могу бити пређене само у подкласи ако су означене вируталном, абстрактном или пређи преко модификатором.[8] Алтернатива за прелазак преко јескривање наслеђеног кода.

  • Поновна употреба кода

Наслеђивање имплементације је механизам којим подкласа поново користи код у родитељској класи. Подкласа садржи све операције родитељске класе, али подкласа може прећи преко неких или свих операција , замењујући имплементацију родитељске класе, својом имплементацијом.

У пратећем Пајтоновом примеру, подкласа  CubeSumComputer прегазила је transform() методу родитељске класе  SquareSumComputer. Родитељска класа садржи операције за израчунавање суме корена између два броја. Подкласа користи све функције родитељске класе са изузетком операције која трансформише број у квадат тог броја, та функција је замењена операцијом која трансформише број у куб. Подкласа изралунава суму кубова између два броја.

class SquareSumComputer(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def transform(self, x):
        return x * x

    def inputs(self):
        return range(self.a, self.b)

    def compute(self):
        return sum(self.transform(value) for value in self.inputs())

class CubeSumComputer(SquareSumComputer):
    def transform(self, x):
        return x * x * x

У већини квартала, класно наслеђивање има једину намену у виду реупотребе. Примарна брига је та што наслеђена имплементација не обезбеђује уверење у полиморфну заменску моћ- инстанца поновне употребе класе не може неминовно бити замењена за инстанцу наслеђене класе. Алтернативни начин, делегација, захтева већи напор од стране програма, али избегава питање замене. У C++ скривено наслеђивање може бити коришћено у форми ''наслеђене имплементације'' без замене. Док јавно наслеђивање представља ''је-а'' везу и делегација представља ''има-a'' везу, скривено (и осигурано) наслеђивање се може посматрати као веза ''јесте имплементирано у завистности од '' [9]

Још једно често коришћење наслеђивања јесте да би се гарантовало да класе одржавају одређени заједнички приступ; тј. да имплементирају исте методе. Родитељска класа може бити комбинација имплементираних операција и операција које су имплементиране у дечијим класама. Често, не постоји приступ промене између два подтипа и под типа - понашање дечије имплементације је описано њеном родитељском класом.[10]

Наслеђивање против подтипова[уреди]

Наслеђивање је слично али и различито од подтипског полиморфизма.[11]  Подтип омогућава да дати тип буде замењен за други тип или апстракцију, и речено је већ да успоставља "је-а" релацију између подтипа и неке постојеће абстракције, имплицитно или експлицитно, у зависности од програма. Релација се може представити експлицитно уз помоћ наслеђивања у програмима који подржавају наслеђивање као механизам подтипа. На пример, код у C++ успоставља експлицитну релацију наслеђивања између класа B и A, где је B и подкласа и подтип од А, и може бити коришћен као А кад год је B одређено ( уз помоћ референци, индикатора или сопственог објекта)

class A 
{ public:
   void DoSomethingALike() const {}
};

class B : public A 
{ public:
   void DoSomethingBLike() const {}
};

void UseAnA(A const& some_A)
{
   some_A.DoSomethingALike();
}

void SomeFunc()
{
   B b;
   UseAnA(b); // b може бити замењено са A.
}

У програмима који не подржавају наслеђивање као подитпски механизам, релација између базне класе и изведене класе је једино релација између имплементација (механизам за поновну употребу кода), упоредно релацији између типова. Наслеђивање, чак и у програмским језицима који подржавају наслеђивање као механизам подтипова, не мора нужно захтевати подтипско понашање. У потпуности је могуће извести класу чији ће се објекат понашати лоше када је коришћен у контексту где је очекивана родитељска класа; видети Лисков принцип замене.[12]

Дизајнирана ограничења[уреди]

Коришћење наслеђивања екстензивно у дизајнирању програма намеће нека ограничења.

На пример, размотримо класу Person која садржи име особе, датум рођења, адресу и број телефона. Можемо дефинисати подкласу класе Person и назвати је Student која садржи просек оцена из класе Person, и другу подкласу класе Person коју називамо Employee која садржи наслов посла, запослене и плату из класе Person.

У дефинисању ове хијерархије наслеђивања, већ смо дефинисали нека ограничења, али не сва ограничења која су неопходна:

Усамљеност
Користећи једно наслеђивање, подкласа може наследити од само једне суперкласе. Настављајући горе наведени пример, Person класа може бити или Student или Employee, али не оба. Користећи мултиплативно наслеђивање при парцијалном решавању овог проблема,  као један се може дефинисати StudentEmployee класа која наслеђује из обе класе Stuent и Employee. У већини имплементација, се само једном може наследити из сваке суперкласе, и то тако, да не подржава случајеве када студент има два посла или похађа две институције. Модел наслеђивања доступан у Ајфелу чини ово могућим кроз подршку за понављачко наслеђивање.
Статичност
Хијерархија наслеђивања неког објетка је фиксирана у инстанци када је тип објекта означен и не мења се током времена. На пример, графиконско наслеђивање не дозвољава Student објекту да постане Employe објекат док је  задржана родитељска класа Person. (Овакав тип понашања, може бити достигнуто уз помоћ декоратора изума. Неколицина је критиковала наслеђивање, тврдећи да ограничи девелопере у њихове оригиналне дизајнерске стандарде  .[15]
Видљивост
Кад год код клијента има приступ неком објекту, он уствару има приступ свим објектима суперкласе. Чак и ако суперкласа није декларисана јавно, клијент и даље може одредити објекат суперкласе. На пример, не постоји много начина дати функцију  извршиоца  студентовој(класа Student) средњој оцени и  препис без давања приступа тој функцији за све персоналне типове сачуване у студентовој Person суперкласи. Много модерних језика, укључујући C++ и Јаву, има „заштитни „приступ модификатора који омогућује подкласама присутуп подацима, без увођења било каквог кода изван ланца наслеђивања за приступ.

Композитна поновна употреба принципа је замена за наслеђивање. Ова техника подржава полиморфизам и поновно коришћење кода различитих понашања од примарне класе у хијерархији и укључујући специфично понашање класа потребних у било каквој бизнис-доменској класи. Овај приступ избегава статичну природу хијерархије класе дозвољавајући промену понашања у брзом времену и дозвољава једној класи да имплеметира понашање („буфет стил“), уместо да буде ограничена на понашање родитељских класа.

Питања и алтернативе[уреди]

Наслеђивање имплементације је контроверза између програмера и теоретичара објектног-оријентисаног програмирања настала у 1990-им. Између њих су аутори дизајнерских изума, који су подржали приступ наслеђивања,  и услуге композиције преко наслеђивања. На пример декораторни изум (као што је споменуто) је предложен да се превазиђе статичка природа наслеђивања између класа. Као више фундаментално решење за исти проблем, програмирање оријентисано на улогама уводи посебну релацију, одиграну од стране, комбинујући својства наслеђивања и композиције у нови концепт.

Према речима Алена Холуба, главни проблем са наслеђивањем имплементације је то што подразумева непотребно заустављање (квачило) у форми „Крхка базна класа- проблем:[4]модификација имплементације базне класе може случајно изазвати промену у суперкласама.[15] Другачије речено, ово је „Наслеђивање зауставља енкапсулацију „.[13] Проблем јасно избија на површину у отвореним објектно-оријентисаним системима као што су оквири, где је очекивано да клијентни код наследи од система-испоручене класе и након тога замени системске класе у својим алгоритмима  .[4]

Наводно, иноватор Јаве Џејмс Гозлинг је био против наслеђивања имплементације, говорећи да он неће учествовати у редизајнирању Јаве.[14] Програмски дизајн који раздваја наслеђивање од подтипова (наслеђивање приступа) се појавило још у раним 90-им;[15] модрни пример овога је програмски језик Гоу.

Комплексно наслеђивање, или наслеђивање коришћено са недовољно сложеним дизајном, може довести до такозванихјо-jo проблема.

Други проблем са наслеђивањем је то што подкласа мора бити дефинисана у коду, што значи да корисници програма не могу додавати нове подкласе.

Види још[уреди]

Напомене[уреди]

  1. Ово је генерално истина само у статично-писаним, класно-оријентисаним објектно оријентисаним програмима као што су C++, C#, Јава, и Скала.

Референце[уреди]

  1. Mintz & Ekendahl (2006). стр. 22.
  2. Cook, William R.; Hill, Walter; Canning, Peter S. (1990).
  3. Cardelli, Luca (1993).
  4. Mikhajlov, Leonid; Sekerinski, Emil (1998).
  5. Tempero, Ewan; Yang, Hong Yul; Noble, James (2013).
  6. "Inheritance in C++".
  7. E Balagurusamy (2010).
  8. override(C# Reference)
  9. "GotW #60: Exception-Safe Class Design, Part 2: Inheritance".
  10. Dr. K. R. Venugopal, Rajkumar Buyya (2013).
  11. Cook, Hill & Canning (1990)
  12. Mitchell, John (2002). "10 "Concepts in object-oriented languages"".
  13. Seiter, Linda M.; Palsberg, Jens; Lieberherr, Karl J. (1996).
  14. Holub, Allen (1 August 2003).
  15. America, Pierre (1991).

Литература[уреди]

  • Mike Mintz, Robert Ekendahl (2006). Hardware Verification with C++: A Practitioner’s Handbook. United States of America: Springer. ISBN 0-387-25543-5.