Кіріспе
Нысанға бағдарланған бағдарламалауда нысанды көшіру – бұрыннан бар нысанның күйіне сүйене отырып, жаңа нысанды құру және инициализациялау амалы. Көшіруді іске асырудың әртүрлі жолдары бағдарламашының дұрыс және тиімді компьютерлік бағдарлама жазу үшін түсінуі қажет салдарларға ие. Көшіру бастапқы нысанның ішкі күйі арқылы бейнеленген, оның күйін пайдалануға және тіпті бастапқы нысанға әсер етпей өзгертуге мүмкіндік береді.
In object oriented programming, object copying is the act of creating and initializing a new object based on an existing object's state. The various ways to implement copy have implications that a programmer needs to understand in order to write a computer program that is correct and performant. Copying allows for the emergent state of the original object represented by its internal state to be used and even modified without affecting the original object.
Стратегиялар
Жалпы, объект монолиттік түсінік сияқты көрінеді, бірақ ішкі құрылымы күрделі деректер – күй ағашынан тұрады. Бағдарламалық қажеттіліктерге және орындалу кезіндегі шығындарға байланысты осы ішкі күйді көшіру үшін бірнеше стратегиялар жасалған. Ең алғаш талқыланғандары – "Smalltalk 80" терминологиясымен белгілі, көшірудің беткей және терең түрлері. Объектілерді теңдік бойынша салыстыруда да осындай айырмашылық сақталады. Екі объектінің тең болуы үшін олардың күйі мағыналы түрде бірдей болуы керек. Екі объектінің өрістері субъектілерге енбей тең болса, олар тең деп есептелуі мүмкін. Немесе, егер объект ағашының барлық деңгейінде күйі бірдей болса ғана тең деп саналады. Егер екі айнымалыда бірдей сілтеме мәні болса, онда олар бір объектіні көрсетеді, бұл теңдіктен де нақтырақ.
Анықтамалық көшірме
Тіпті көлеңкелі көшірмеден де беткей, сілтемені көшіру – нысанды көшірудің бір түрі. Бұл стратегия көбінесе нысанды әдіске жіберу үшін қолданылады. Сілтеме мәні (вероятно, мекенжайы) мәні бойынша беріледі – сілтеме мәнінің көшірмесі.
Терең көшірме
Терең көшіру барлық бағынысты нысандардың күйін көшіруді қамтиды, нысандарға сілтемелерді ағаштың әр деңгейінде рекурсивті түрде қарап, бастапқы нысанның күйін жасайды және жаңа нысандар мен өрістерді көшіреді. Бастапқы немесе көшірілген нысанның, соның ішіндегі ішкі нысандарының өзгеруі екіншісіне әсер етпейді, себебі олардың арасында ортақ ештеңе жоқ.
Гибридті
Күрделірек жағдайларда, көшірмедегі кейбір өрістер бастапқы нысанмен ортақ мәндерге ие болуы керек (шамалы көшірме сияқты), бұл қауымдастырылған қатынасқа сәйкес келеді; ал кейбір өрістер көшірмелерге ие болуы керек (терең көшірме сияқты), бұл жиынтықталған қатынасқа сәйкес келеді. Мұндай жағдайларда көшірудің жекелеген әдісін жасау қажет болады; бұл мәселе және оның шешімі Smalltalk 80-ге дейін барады. Балама ретінде, өрістер шамалы көшірме немесе терең көшірме жасауды талап ететінін белгілеуге болады, содан кейін көшіру операциялары автоматты түрде жасалады (салыстыру операциялары үшін де осылай). Дегенмен, көптеген объектіге бағытталған тілдерде мұндай мүмкіндік жоқ, бірақ Eiffel тілінде ішінара қолдау бар.
Жалқау көшірмелеу
Жалқау көшірме, жазу арқылы көшірумен байланысты, терең көшірмені іске асырудың бір тәсілі. Объектіні бастапқы көшіргенде, салыстырмалы түрде жылдам, терең емес көшірме жасалады. Деректерді қанша объекті бөлісіп отырғанын қадағалау үшін санаушы қолданылады. Бағдарлама объектіні өзгертуді қалағанда, деректер бөлісілген-бөлінбегенін (санаушыны тексеру арқылы) анықтай алады және қажет болған жағдайда терең көшірме жасай алады. Жалқау көшірме терең көшірменің қасиеттерін қамтамасыз етеді, бірақ мүмкіндігі болғанда терең емес көшірменің жылдамдығынан пайдаланады. Оның кемшілігі – санаушының болуынан туындайтын жоғары, бірақ тұрақты бастапқы шығындар. Айналма сілтемелер мәселе тудыруы мүмкін.
Мысалдар
Жалпы, объектіге бағытталған бағдарламалау тілі объектіні көшірудің әдісін ұсынады. Бағдарламашы өзі анықтаған объектінің қалай көшірілетінін, екі объектінің тең екенін, салыстырылатынын және т.б. анықтағандай анықтауы керек. Кейбір тілдер бір көшіру операциясын немесе бөлек беткейлік және тереңдік операцияларын анықтай отырып, беткейлік немесе тереңдік стратегиясының біреуін қолдайды. Көптеген тілдер белгілі бір стандартты мінез-құлықты қамтамасыз етеді.
Жава
Java-да объектіге әрқашан сілтеме арқылы жанама түрде қол жеткізіледі. Объект ешқашан жасырын түрде жасалмайды, керісінше, әрқашан сілтеме айнымалысы арқылы беріледі немесе тағайындалады. Параметрлер мәні бойынша беріледі, бірақ берілгені сілтеменің мәні. Java виртуалды машинасы қоқыс жинауды басқарады, сондықтан объектілер олар қол жетімді болмай қалғаннан кейін тазартылады. Тіл объектіні көшірудің автоматты тәсілін ұсынбайды. Көшіру әдетте `clone` әдісі арқылы жүзеге асырылады. Бұл әдіс көшірме алу үшін әдетте ата-ана класының `clone` әдісін шақырады, содан кейін кез келген бейімделген көшіру процедураларын орындайды. Соңында, бұл ең жоғарғы объектінің (`Object`) `clone` әдісіне жетеді, ол объект сияқты бір сыныптың жаңа данасын жасайды және барлық өрістерді жаңа данаға көшіреді (беткей көшірме). Егер бұл әдіс қолданылса, класс интерфейсті жүзеге асыруы керек, әйтпесе "Clone Not Supported Exception" тасталады. Ата-ана класынан көшірме алғаннан кейін, кластың өзінің `clone` әдісі терең көшіру (яғни, объект көрсеткен кейбір құрылымдарды дубликациялау) немесе жаңа данаға жаңа бірегей идентификатор беру сияқты жеке көшіру мүмкіндігін қамтамасыз ете алады. `clone` әдісінің қайтарым түрі – `Object`, бірақ `clone` әдісін жүзеге асырушылар Java-ның ковариантты қайтарым түрлерін қолдауына байланысты клондалатын объектінің түрін жаза алады. `clone` әдісін пайдаланудың бір артықшылығы – ол ауыстырылатын әдіс болғандықтан, кез келген объектіге `clone` шақыруға болады, және ол өзінің класының `clone` әдісін қолданады, шақыру кодын сол кластың қандай екенін білуге міндеттемейді (бұл көшірме конструктормен қажет болар еді). Бір кемшілігі – абстрактілі типте `clone` әдісіне жиі қол жеткізу мүмкін емес. Java-дағы көптеген интерфейстер мен абстрактілі кластар жалпыға қолжетімді `clone` әдісін анықтамайды. Сондықтан, `clone` әдісін пайдаланудың жалғыз жолы – объектінің класы белгілі болса, бұл мүмкін болатын ең жалпы типті пайдаланудың абстракция принципіне қайшы келеді. Мысалы, егер Java-да `List` сілтемесі болса, онда сол сілтемеге `clone` шақыруға болмайды, өйткені `List` ашық `clone` әдісін анықтамайды. `List` жүзеге асырулары, мысалы, `ArrayList` және `LinkedList` әдетте `clone` әдістеріне ие, бірақ объектінің сынып түрін алып жүру ыңғайсыз және нашар абстракция. Java-да объектілерді көшірудің тағы бір жолы – оларды `Serializable` интерфейсі арқылы сериалдау. Бұл әдетте тұрақтылық және сымдық протокол мақсаттарында қолданылады, бірақ ол объектілердің көшірмелерін жасайды және, `clone` әдісінен айырмашылығы, объектілердің циклдік графиктерін оңай басқаратын терең көшірме бағдарламашының ең аз күш-жігерімен қол жетімді. Бұл екі әдіс те айтарлықтай мәселеге тап болады: конструктор `clone` немесе сериалдау арқылы көшірілген объектілер үшін қолданылмайды. Бұл дұрыс инициализацияланбаған деректермен қателерге әкелуі мүмкін, соңғы мүше өрістерін пайдалануға жол бермейді және техникалық қызмет көрсетуді қиындатады. Кейбір утилиталар терең көшіру кітапханасы сияқты, терең көшіру объектілерін рефлексия арқылы пайдалану арқылы осы мәселелерді шешуге тырысады.
Эйфел
Эйфельдегі орындалу кезіндегі объектілерге сілтемелер арқылы немесе оларды пайдаланатын объектілерге енгізілген өрістер ретінде кеңейтілген объектілер арқылы жанама түрде қол жеткізуге болады. Яғни, объектінің өрістері сырттан немесе іштен сақталады. Эйфель класы ANY объектілерді көшіру және клондау үшін, соның ішінде терең және беткей көшіру мүмкіндіктерін қамтиды. Барлық Эйфель кластары ANY-дан мұра алады, сондықтан бұл мүмкіндіктер барлық кластарда қолжетімді және сілтемелік объектілерге де, кеңейтілген объектілерге де қатысты. Көшіру мүмкіндігі бір объектіден екіншісіне беткей, өріс бойынша көшірме жасауды жүзеге асырады. Бұл жағдайда жаңа объекті құрылмайды. Егер y x-ке көшірілсе, көшіру қолданылғанға дейін y сілтеме берген объектілер, көшіру аяқталғаннан кейін x сілтемесінде де болады. y-тің беткей дубликатын құру үшін егіз мүмкіндігі қолданылады. Бұл жағдайда бастапқы объектінің өрістерімен бірдей өрістері бар бір жаңа объекті құрылады. Егіз мүмкіндігі, қажет болған жағдайда ANY-дың ұрпақтарында қайта анықталуы мүмкін көшіру мүмкіндігіне сүйенеді. Егіздің нәтижесі Current сияқты бекітілген типте болады. Терең көшіру және терең егіздерді құру терең көшіру және терең егіз мүмкіндіктерін пайдалану арқылы жүзеге асырылады, олар да ANY класынан мұрагерлікпен алынады. Бұл мүмкіндіктер бүкіл объекті құрылымындағы барлық объектілерді дубликациялағандықтан көптеген жаңа объектілерді құруға әкелуі мүмкін. Жаңа дубликатталған объектілер құрылғандықтан, тек қолданыстағы объектілерге сілтемелерді көшірудің орнына, терең операциялар беткей операцияларға қарағанда оңайрақ өнімділік мәселелеріне себеп болуы мүмкін.
С#
C#-та ICloneable интерфейсін пайдалану орнына, рефлексияны қолдана отырып, терең көшірме жасау үшін жалпы кеңейтім әдісін қолдануға болады. Бұл екі артықшылық береді: Біріншіден, ол кез келген нысанды жеке-жеке қасиеттер мен айнымалыларды қолмен көрсетусіз көшіруге мүмкіндік береді. Екіншіден, тип жалпы болғандықтан, компилятор көшірме алатын нысан мен бастапқы нысанның бірдей типте болуын қамтамасыз етеді.
C-мақсаты
Objective-C тілінде copy және mutableCopy әдістері барлық объектілерге мұра болып келеді және көшірме жасау үшін қолданылады; екіншісі бастапқы объектінің өзгертілетін түрін жасауға арналған. Бұл әдістер өз кезегінде көшіруді жүзеге асыру үшін тиісінше copyWithZone және mutableCopyWithZone әдістерін шақырады. Объект көшіріле алуы үшін сәйкес copyWithZone әдісін іске асыруы керек.
ОКамл
OCaml-де кітапхана функциясы Oo.copy объектінің беткей көшірмесін жасайды.
Python-тың атауы
Python-да кітапхананың copy модулі copy және deepcopy функциялары арқылы объектілердің жайнақ көшірмесі мен терең көшірмесін ұсынады. Бағдарламашылар объектіде көшірме және deepcopy атты арнайы әдістерді анықтап, көшіруді өзгерте алады.
Руби
Ruby-де барлық нысандар көшірме жасау үшін екі әдіс – clone және dup мұралайды. Бұл екі әдіс бір-бірінен соңғысының айырмашылығында: clone нысанның «ластанған» күйін, қатырылған күйін және кез келген жеке әдістерін көшіреді, ал dup тек «ластанған» күйін ғана көшіреді. Терең көшірмелерді нысанның байт ағынын төгіп, жүктеу арқылы немесе YAML форматында сақтау арқылы жасауға болады. Балама ретінде, нысандық графтардың терең көшірмесін жасау үшін Deep Dive gem қолдануға болады.
Perl (жазу)
Perl-де ұялы құрылымдар сілтемелер арқылы сақталады, сондықтан бағдарламашы бүкіл құрылымды аралап, деректерге қайта сілтеме жасауға немесе Storable модулінен dclone функциясын пайдалануға болады.
VBA
VBA-да Object типіндегі айнымалыларға тағайындау – бұл жай көшірме, ал қалған барлық типтер үшін (сандық типтер, String, пайдаланушы анықтаған типтер, массивтер) – терең көшірме болып табылады. Сондықтан, тағайындау үшін Set кілт сөзі жай көшірмені, ал (керек болмаса) Let кілт сөзі терең көшірмені білдіреді. VBA-да Object-тердің терең көшірмелерін жасау үшін кіріктірілген әдіс жоқ.