Кіріспе
Сыныптарды иерархиядан алу және оларды иерархияға ұйымдастыру процесі. Объектіге бағытталған бағдарламалауда мұрагерлік – объект немесе сыныпты басқа объектіге (прототипке негізделген мұрагерлік) немесе сыныпқа (сыныпқа негізделген мұрагерлік) негіздеу механизмі, осы арқылы ұқсас іске асыру сақталады. Сондай-ақ, мұрагерлік – суперсынып немесе базалық сынып сияқты қолданыстағы сыныптардан жаңа сыныптарды (кіші сыныптарды) тудыру және оларды сыныптардың иерархиясына құру болып табылады. C++ сияқты сыныптық объектіге бағытталған тілдердің көпшілігінде мұрагерлік арқылы құрылған объект, "балалық объект", "аталық объектінің" барлық қасиеттері мен мінез-құлқын мұраға алады, конструкторлар, деструкторлар, жүктемелі операторлар және базалық сыныптың дос функцияларынан басқа. Мұрагерлік бағдарламашыларға қолданыстағы сыныптарға негізделген сыныптарды жасауға, жаңа іске асыруды анықтауға, сонымен бірге бірдей мінез-құлқыны сақтап (интерфейсті іске асыру), кодты қайта пайдалануға және қоғамдық сыныптар мен интерфейстер арқылы бастапқы бағдарламалық жасақтаманы тәуелсіз кеңейтуге мүмкіндік береді. Мұрагерлік арқылы объектілер мен сыныптардың қарым-қатынастары бағытталған ациклдік графты құрайды. Мұрагерлік класты оның аталық класының немесе суперкласының кіші класы деп атайды. "Мұрагерлік" термині сыныпқа және прототипке негізделген бағдарламалау үшін де қолданылады, бірақ тар мағынасында бұл термин сыныпқа негізделген бағдарламалау үшін ғана қолданылады (бір сынып екіншісінен мұраға алады), ал прототипке негізделген бағдарламалаудағы сәйкес техника делегация деп аталады (бір объект екіншісіне өкілеттік береді). Сыныпты өзгерту мұрагерлік үлгілерін қарапайым желілік интерфейс параметрлеріне сәйкес алдын ала анықтауға болады, осылайша тілдер арасындағы үйлесімділік сақталады. Мұрагерлікті субтиптеумен шатастыруға болмайды. Кейбір тілдерде мұрагерлік пен субтиптеу сәйкес келеді, ал басқаларында олар өзгеше; жалпы алғанда, субтиптеу "болып табылады" қатынасын белгілейді, ал мұрагерлік тек іске асыруды қайта пайдаланады және синтаксистік қатынасты орнатады, міндетті түрде семантикалық қатынас емес (мұрагерлік мінез-құлқының субтиптеуін қамтамасыз етпейді). Бұл ұғымдарды ажырату үшін субтиптеу кейде интерфейс мұрагерлігі деп аталады (типтік айнымалылардың мамандануы да субтиптеу қатынасын тудырады дегенді мойындамай), ал мұнда анықталған мұрагерлік – іске асыру мұрагерлігі немесе код мұрагерлігі деп аталады. Дегенмен, мұрагерлік – субтиптік қатынастарды орнату үшін жиі қолданылатын механизм. Мұрагерлік объектілік композицияға қарама-қарсы қойылады, онда бір объект басқа объектіні (немесе бір сыныптың объектілері басқа сыныптың объектілерін) қамтиды; мұрагерлікке қатысты композицияны қараңыз. Композиция "құрамында бар" қатынасын білдіреді, ал субтиптеу "болып табылады" қатынасын білдіреді.
In object oriented programming, inheritance is the mechanism of basing an object or class upon another object (prototype based inheritance) or class (class based inheritance), retaining similar implementation. Also defined as deriving new classes (sub classes) from existing ones such as super class or base class and then forming them into a hierarchy of classes. In most class based object oriented languages like C++, an object created through inheritance, a "child object", acquires all the properties and behaviors of the "parent object", with the exception of: constructors, destructors, overloaded operators and friend functions of the base class. Inheritance allows programmers to create classes that are built upon existing classes, to specify a new implementation while maintaining the same behaviors (realizing an interface), to reuse code and to independently extend original software via public classes and interfaces. The relationships of objects or classes through inheritance give rise to a directed acyclic graph. An inherited class is called a subclass of its parent class or super class. The term "inheritance" is loosely used for both class based and prototype based programming, but in narrow use the term is reserved for class based programming (one class inherits from another), with the corresponding technique in prototype based programming being instead called delegation (one object delegates to another). Class modifying inheritance patterns can be pre defined according to simple network interface parameters such that inter language compatibility is preserved. Inheritance should not be confused with subtyping. In some languages inheritance and subtyping agree, whereas in others they differ; in general, subtyping establishes an is a relationship, whereas inheritance only reuses implementation and establishes a syntactic relationship, not necessarily a semantic relationship (inheritance does not ensure behavioral subtyping). To distinguish these concepts, subtyping is sometimes referred to as interface inheritance (without acknowledging that the specialization of type variables also induces a subtyping relation), whereas inheritance as defined here is known as implementation inheritance or code inheritance. Still, inheritance is a commonly used mechanism for establishing subtype relationships. Inheritance is contrasted with object composition, where one object contains another object (or objects of one class contain objects of another class); see composition over inheritance. Composition implements a has a relationship, in contrast to the is a relationship of subtyping.
Тарих
1966 жылы Тони Хоар деректер туралы бірнеше ой-пікірлерді ұсынды, әсіресе деректердің подкластары туралы идеяны ұсынды – ортақ қасиеттері бар, бірақ түрлендіргіш белгісімен және осы түрлендіргішке тән жеке өрістері бар деректер түрлері. Осыған ықпал етіп, 1967 жылы Оле Йохан Даль мен Кристен Найгаард әртүрлі кластарға жататын, бірақ ортақ қасиеттері бар объектілерді анықтауға мүмкіндік беретін дизайнды ұсынды. Ортақ қасиеттер жоғары класта жинақталды, және әр жоғары кластың өзі де жоғары кластың болуы мүмкін. Осылайша, подкластың мәндері әртүрлі жоғары кластарға жататын бірнеше префикс бөліктерінен және подкласқа жататын негізгі бөліктен тұратын күрделі объектілер болды. Бұл бөліктердің барлығы біріктірілді. Күрделі объектінің атрибуттары нүктелік нотация арқылы қолжетімді болатын. Бұл идея алғаш рет Simula 67 бағдарламалау тілінде қабылданып, кейін Smalltalk, C++, Java, Python және көптеген басқа тілдерге тарады.
Кіші сыныптарға жатпайтын кластар
Кейбір тілдерде сыныпты тармақтап жазуға болмайтын сынып деп жариялауға болады, сынып жарияланымына белгілі бір сынып модификаторларын қосу арқылы. Мысалдарға Java және C++11-дегі `final` түйін сөзі немесе C#-тағы `sealed` түйін сөзі жатады. Мұндай модификаторлар сынып түйін сөзінен және сынып идентификаторы жарияланымынан бұрын сынып жарияланымына қосылады. Мұндай тармақтап жазуға болмайтын сыныптар қайта пайдалануды шектейді, әсіресе егер дамытушылар тек алдын ала құрастырылған бинарлық файлдарға қол жеткізе алса, бастапқы кодқа емес. Тармақтап жазуға болмайтын сыныптың тармақтары болмайды, сондықтан компиляция кезінде осы сыныптың нысандарына сілтемелер немесе көрсеткіштер осы сыныптың мысалдарына сілтеме жасайды екені анық көрінеді, ал тармақтардың мысалдарына (олар жоқ) немесе жоғары сыныптардың мысалдарына (анықтама типін шығару типтік жүйені бұзады) сілтеме жасамайды. Орындалу алдында сілтеме жасалатын нысанның нақты типі белгілі болғандықтан, ерте байланысты (статикалық жіберу деп те аталады) кеш байланыстың орнына (динамикалық жіберу деп те аталады) пайдалануға болады, бұл қолданылатын бағдарламалау тілінде көп мұрагерлік немесе тек бір мұрагерлік қолдау көрсетіледі ме дегенге байланысты бір немесе бірнеше виртуалды әдіс кестелерін іздеуді қажет етеді.
Қайталана алмайтын әдістер
Сыныптар тұқымдас бола алмағаны сияқты, әдіс жарияланымдарында әдістің қайта жазылуын (яғни, бірдей атауы мен типтік қолтаңбасы бар жаңа функциямен субкласс ішінде алмастырылуын) болдырмайтын әдіс модификаторлары болуы мүмкін. Жеке әдіс оның мүше функциясы болып табылатын сыныптан басқа сыныптарға қолжетімді болмағандықтан қайта жазыла алмайды (бірақ бұл C++ үшін дұрыс емес). Java-дағы `final` әдісі, C#-тағы `sealed` әдісі немесе Eiffel-дегі `frozen` мүмкіндігі қайта жазыла алмайды.
Виртуалды әдістер
Егер жоғары класс әдісі виртуалды әдіс болса, онда жоғары класс әдісін шақырулар динамикалық түрде орындалады. Кейбір тілдерде әдіс виртуалды деп тікелей жариялануы керек (мысалы, C++), ал басқаларында барлық әдістер виртуалды болады (мысалы, Java). Виртуалды емес әдісті шақыру әрқашан статикалық түрде орындалады (яғни, функцияны шақыру мекенжайы компиляция кезінде анықталады). Статикалық орындау динамикалық орындаудан жылдам және кодты ішке енгізу сияқты оңтайландыруларға мүмкіндік береді.
Қолданбалар
Мұрагерлік екі немесе одан көп сыныптардың арасында байланыс орнату үшін қолданылады.
Басымдық
Көптеген объектіге бағытталған бағдарламалау тілдері сыныпқа немесе объектіге мұраға алған аспектінің – әдетте мінез-құлықтың – іске асылуын алмастыруға мүмкіндік береді. Бұл процесс қайта жазу деп аталады. Қайта жазу бір қиындық тудырады: мұрагерлік сыныптың бір данасы қандай мінез-құлықты пайдаланады – өзінің сыныбының бөлігін, әлде ата-ана (негізгі) сыныбының нұсқасын ба? Жауап бағдарламалау тілдеріне қарай өзгереді, ал кейбір тілдер нақты бір мінез-құлықты қайта жазуға болмайтынын және негізгі сыныптың анықтамасы бойынша жұмыс істеуін көрсетуге мүмкіндік береді. Мысалы, C# тілінде негізгі әдіс немесе қасиет тек виртуалды, абстрактілі немесе қайта жазу модификаторымен белгіленген жағдайда ғана кіші сыныпта қайта жазылуы мүмкін, ал Java сияқты бағдарламалау тілдерінде басқа әдістерді қайта жазу үшін әртүрлі әдістерді шақыруға болады. Мұның орнына мұраға алған кодты жасыруға болады.
Тұқым қуалау мен субтиптеу
Тұқым қуалау субтипілеуге ұқсас, бірақ одан өзгеше. (Коннотация мен денотацияны салыстырыңыз.) Кейбір объектіге бағытталған бағдарламалау тілдерінде кодты қайта пайдалану және субтипілеу ұғымдары бірдей, себебі субтипті жариялаудың жалғыз жолы – басқа сыныптың іске асырылуын мұралап алатын жаңа сыныпты анықтау.
Мәселелер мен баламалар
Іске асыру мұрагерлігі 1990 жылдардан бері бағдарламашылар мен объектіге бағытталған бағдарламалау теориясының мамандары арасында пікірталас тудырып келеді. Олардың ішінде Дизайн үлгілерінің авторлары да бар, олар интерфейс мұрагерлігін жақтасады және мұрагерлікке қарағанда композицияны артық көреді. Мысалы, кластар арасындағы мұрагерлікке тән статикалық сипатты жою мақсатында декоратор үлгісі (жоғарыда айтылғандай) ұсынылған. Осы мәселенің тағы бір түбегейлі шешімі ретінде рөлге бағытталған бағдарламалау мұрагерлік пен композицияның қасиеттерін біріктіретін, жаңа тұжырымдаманы құратын, "ойнайтын рөл" деп аталатын ерекше қатынасты енгізеді. Аллен Холубтың пікірінше, іске асыру мұрагерлігінің басты мәселесі – қажетсіз байланыстың туындауы, яғни "нашар базалық класс мәселесі": базалық кластың іске асырылуына енгізілген өзгерістер, кіші кластардың күтпеген әрекеттеріне әкелуі мүмкін. Интерфейстерді қолдану арқылы бұл мәселенің алдын алуға болады, себебі ортақ іске асырылу болмайды, тек API ғана қолданылады. Бұл жағдайды "мұрагерлік инкапсуляцияны бұзады" деп те айтуға болады. Бұл мәселе ашық объектіге бағытталған жүйелерде, мысалы, фреймворктерде айқын көрінеді, онда клиенттік код жүйемен ұсынылған кластардан мұрагерлік алып, содан кейін жүйе кластарын өз алгоритмдерінде алмастыруы күтіледі. Java тілін ойлап тапқан Джеймс Гослинг, Java тілін қайта жобаласа, іске асыру мұрагерлігін енгізбейтінін айтып, оған қарсы пікір білдірген. Мұрагерлікті субтиптеуден ажыратып қарастыратын тілдік дизайндар (интерфейс мұрагерлігі) 1990 жылға дейін пайда болған, ал қазіргі заманғы мысалы – Go бағдарламалау тілі. Күрделі мұрагерлік немесе жетілмеген дизайн шеңберінде қолданылатын мұрагерлік "йо-йо" мәселесіне әкелуі мүмкін. 1990 жылдардың соңында мұрагерлік бағдарламаларды құрылымдаудың басты тәсілі ретінде қолданылған кезде, әзірлеушілер жүйе функционалдығы өскен сайын кодты мұрагерліктің көптеген қабаттарына бөлуге бейім болды. Егер әзірлеу тобы бірнеше қабатты мұрагерлікпен біріктірсе, нәтижесінде көптеген өте жұқа код қабаттары пайда болады, олардың көптеген қабаттарында тек 1 немесе 2 жол код болады. Мұндай көп қабаттар жөндеуді қиынға айналдырады, себебі қай қабатты жөндеу керектігін анықтау қиын. Мұрагерлікпен байланысты тағы бір мәселе – кіші кластарды кодта анықтау қажеттігі, яғни бағдарлама пайдаланушылары орындалу кезінде жаңа кіші кластарды қоса алмайды. Басқа дизайн үлгілері (мысалы, Entity–component–system) бағдарлама пайдаланушыларына орындалу кезінде бір объектінің түрлерін анықтауға мүмкіндік береді.