TIE Ohjelmistojen suunnittelu. Kopiointia ja sijoittelua

Samankaltaiset tiedostot
TIE Ohjelmistojen suunnittelu. Kopiointia ja sijoittelua

TIE Ohjelmistojen suunnittelu

Osoitin ja viittaus C++:ssa

815338A Ohjelmointikielten periaatteet Harjoitus 5 Vastaukset

812347A Olio-ohjelmointi, 2015 syksy 2. vsk. X Poikkeusten käsittelystä

TIE Ohjelmistojen suunnittelu. Luento 8..9: moniperintä

T Olio-ohjelmointi Osa 5: Periytyminen ja polymorfismi Jukka Jauhiainen OAMK Tekniikan yksikkö 2010

C++11 lambdat: [](){} Matti Rintala

Rajapinta (interface)

Olio-ohjelmointi Syntaksikokoelma

Operaattoreiden ylikuormitus. Operaattoreiden kuormitus. Operaattoreiden kuormitus. Operaattoreista. Kuormituksesta

4. Luokan testaus ja käyttö olion kautta 4.1

16. Javan omat luokat 16.1

13 Operaattoreiden ylimäärittelyjä

TIE Ohjelmistojen suunnittelu

Virtuaalifunktiot ja polymorfismi

Sisällys. Yleistä attribuuteista. Näkyvyys luokan sisällä. Tiedonkätkentä. Aksessorit. 4.2

Demo 6 vastauksia. 1. tehtävä. #ifndef #define D6T1 H D6T1 H. #include <iostream> using std::ostream; using std::cout; using std::endl;

Kooste. Esim. Ympyrän keskipiste voidaan ajatella ympyrän osaksi.

12 Mallit (Templates)

Rajapinnat ja olioiden välittäminen

TIE Ohjelmistojen suunnittelu

A) on käytännöllinen ohjelmointitekniikka. = laajennetaan aikaisemmin tehtyjä luokkia (uudelleenkäytettävyys)

Sisällys. 7. Oliot ja viitteet. Olion luominen. Olio Java-kielessä

Javan perusteita. Janne Käki

Sisällys. Yleistä attribuuteista. Näkyvyys luokan sisällä ja ulkopuolelta. Attribuuttien arvojen käsittely aksessoreilla. 4.2

JAVA-PERUSTEET. JAVA-OHJELMOINTI 3op A JAVAN PERUSTEET LYHYT KERTAUS JAVAN OMINAISUUKSISTA JAVAN OMINAISUUKSIA. Java vs. C++?

Olio-ohjelmointi Javalla

ITKP102 Ohjelmointi 1 (6 op)

C++11 Syntaksi. Jari-Pekka Voutilainen Jari-Pekka Voutilainen: C++11 Syntaksi

Kompositio. Mikä komposition on? Kompositio vs. yhteyssuhde Kompositio Javalla Konstruktorit set-ja get-metodit tostring-metodi Pääohjelma

15. Ohjelmoinnin tekniikkaa 15.1

Ohjelman virheet ja poikkeusten käsittely

Olion elinikä. Olion luominen. Olion tuhoutuminen. Olion tuhoutuminen. Kissa rontti = null; rontti = new Kissa();

Metodit. Metodien määrittely. Metodin parametrit ja paluuarvo. Metodien suorittaminen eli kutsuminen. Metodien kuormittaminen

Ohjelmointi 2. Jussi Pohjolainen. TAMK» Tieto- ja viestintäteknologia , Jussi Pohjolainen TAMPEREEN AMMATTIKORKEAKOULU

7. Oliot ja viitteet 7.1

Luokassa määriteltävät jäsenet ovat pääasiassa tietojäseniä tai aliohjelmajäseniä. Luokan määrittelyyn liittyvät varatut sanat:

Sisällys. JAVA-OHJELMOINTI Osa 6: Periytyminen ja näkyvyys. Luokkahierarkia. Periytyminen (inheritance)

Olio-ohjelmoinnissa luokat voidaan järjestää siten, että ne pystyvät jakamaan yhteisiä tietoja ja aliohjelmia.

815338A Ohjelmointikielten periaatteet Harjoitus 3 vastaukset

T Olio-ohjelmointi Osa 3: Luokka, muodostin ja hajotin, this-osoitin Jukka Jauhiainen OAMK Tekniikan yksikkö 2010

C++ rautaisannos. Kolme tapaa sanoa, että tulostukseen käytetään standardikirjaston iostreamosassa määriteltyä, nimiavaruuden std oliota cout:

Sisällys. 9. Periytyminen Javassa. Periytymismekanismi Java-kielessä. Periytymismekanismi Java-kielessä

Sisällys. JAVA-OHJELMOINTI Osa 7: Abstrakti luokka ja rajapinta. Abstraktin luokan idea. Abstrakti luokka ja metodi. Esimerkki

812347A Olio-ohjelmointi, 2015 syksy 2. vsk. VII Suunnittelumallit Adapter ja Composite

Listarakenne (ArrayList-luokka)

Geneeriset luokat. C++ - perusteet Java-osaajille luento 6/7: Template, tyyppi-informaatio, nimiavaruudet. Geneerisen luokan käyttö.

Metodien tekeminen Javalla

812341A Olio-ohjelmointi, IX Olioiden välisistä yhteyksistä

Sisällys. 1. Omat operaatiot. Yleistä operaatioista. Yleistä operaatioista

14. oppitunti. Operaattorin ylikuormitus. Osa. Operaattorin ylikuormittaminen

Mikä yhteyssuhde on?

Jakso 4 Aliohjelmien toteutus

1. Omat operaatiot 1.1

15. Ohjelmoinnin tekniikkaa 15.1

Olio-ohjelmointi 2. välikoe HYV5SN

ITKP102 Ohjelmointi 1 (6 op)

815338A Ohjelmointikielten periaatteet

9. Periytyminen Javassa 9.1

2. Olio-ohjelmoinista lyhyesti 2.1

12. Monimuotoisuus 12.1

9. Periytyminen Javassa 9.1

Luento 4 Aliohjelmien toteutus

812347A Olio-ohjelmointi, 2015 syksy 2. vsk. IX Suunnittelumallit Proxy, Factory Method, Prototype ja Singleton

Osa III. Olioiden luominen vapaalle muistialueelle

TIE Samuel Lahtinen. Lyhyt UML-opas. UML -pikaesittely

Muutamia peruskäsitteitä

1. Olio-ohjelmointi 1.1

Tietueet. Tietueiden määrittely

Tehtävä 1. TL5302 Olio-ohjelmointi Koe Malliratkaisuja. Tässä sekä a)- että b)-kohdan toimiva ratkaisu:

Opintojakso TT00AA11 Ohjelmoinnin jatko (Java): 3 op Pakkaukset ja määreet

Opintojakso TT00AA11 Ohjelmoinnin jatko (Java): 3 op Rajapinnat ja sisäluokat

Operaattoreiden uudelleenmäärittely

Harjoitus Olkoon olemassa luokat Lintu ja Pelikaani seuraavasti:

Osa VII. Mitä mallit ovat ja kuinka niitä käytetään Miksi mallit tarjoavat paremman vaihtoehdon makroille Kuinka luokkamalleja luodaan

Sisällys. 9. Periytyminen Javassa. Periytymismekanismi Java-kielessä. Periytymismekanismi Java-kielessä

Java kahdessa tunnissa. Jyry Suvilehto

Olio-ohjelmointi Suunnittelumallit Proxy, Factory Method, Prototype ja Singleton. 1. Proxy (Edustaja)

Sisällys. 6. Metodit. Oliot viestivät metodeja kutsuen. Oliot viestivät metodeja kutsuen

Oliosuunnitteluesimerkki: Yrityksen palkanlaskentajärjestelmä

812341A Olio-ohjelmointi Peruskäsitteet jatkoa

Opintojakso TT00AA11 Ohjelmoinnin jatko (Java): 3 op Taulukot & Periytyminen

4. Olio-ohjelmoinista lyhyesti 4.1

Ohjelmoinnin jatkokurssi, kurssikoe

12. Monimuotoisuus 12.1

Periytyminen. Luokat ja olio-ohjelmointi

Sisältö. 2. Taulukot. Yleistä. Yleistä

Vertailulauseet. Ehtolausekkeet. Vertailulauseet. Vertailulauseet. if-lauseke. if-lauseke. Javan perusteet 2004

OHJ-1450 Olio-ohjelmoinnin jatkokurssi lisämateriaalia

TIE448 Kääntäjätekniikka, syksy Antti-Juhani Kaijanaho. 27. lokakuuta 2009

Taulukot. Jukka Harju, Jukka Juslin

Ohjelmoinnin peruskurssien laaja oppimäärä

UML ja luokkien väliset suhteet

Rakenteiset tietotyypit Moniulotteiset taulukot

Yleistä. Nyt käsitellään vain taulukko (array), joka on saman tyyppisten muuttujien eli alkioiden (element) kokoelma.

Rajapinnasta ei voida muodostaa olioita. Voidaan käyttää tunnuksen tyyppinä. Rajapinta on kuitenkin abstraktia luokkaa selvästi abstraktimpi tyyppi.

Aalto Yliopisto T Informaatioverkostot: Studio 1. Oliot ja luokat Javaohjelmoinnissa

Osa III. Edelliset kolme lukua ovat käsitelleet viittausten ja osoittimien käyttöä. Tämän luvun aiheita ovat:

Lyhyt kertaus osoittimista

Transkriptio:

TIE-20200 Ohjelmistojen suunnittelu Kopiointia ja sijoittelua 1

Tänään tarjolla Tehdasjuttujen esimerkki (vh4- esimerkkivastaus) Harkkatöiden valmiit koodit vol 2 Kopiointijatkoja 2

Kopiointiin liittyviä kysymyksiä Milloin kopiointia tarvitaan? Mikä on olion kopio? Perusmuuttujat, kopioidaan alkuperäisen muuttujan muistin sisältö uudelle oliolle Olioiden kanssa on mietittävä mitä oikeastaan halutaan kopioida? Kaikkien jäsenmuuttujien sisältö? Kaikkien viitteiden ja osoittimien sisältö? Kaikki rekisteröinnit ja suhteet, joita oliolla on? 3

Mitä eri kopiointitapoja C++:ssa on? 4

Viitekopiointi Kopio luodaan luomalla uusi viite, joka osoittaa samaan olioon (Java, Smalltalk, C#) class CarOwner public: CarOwner( string name, Car* car ); CarOwner( string name, Car* car ); void changecar( Car* newcar ); private: string name_; Car* mycar_; ; class Car ; Car lada( lada ); CarOwner tero( tero, &lada ); CarOwner& ref1 = tero; CarOwner& ref2 = ref1; tero, ref1 ja ref2 viittaavat samaan olioon. Tero:CarOwner ref 1 ref 1 Tero changecar() drive() refuel() Tero:CarOwner Tero changecar() drive() refuel() ref 2 external data (Lada:Car) external data (Lada:Car) 5

Matala kopiointi Matala kopio luodaan kopioimalla kaikkien jäsenmuuttujien arvot. Kopiorakentaja on helppo luoda automaattisesti (kääntäjän toimesta) Ongelmat: luokan ulkopuoliset tiedot, pitääkö luoda kopio myös osoittimien (+viitteiden) päässä olevasta tiedosta, itse osoitin vai kenties jättää osoitin nolla-arvoon? Ennen: Jälkeen: Tero:CarOwner Tero f1() f2() f3() Tero:CarOwner Tero f1() f2() f3() Tero2:CarOwner Tero f1() f2() f3() external data (Lada:Car) external data (Lada:Car) Terosta kloonataan, tämän jälkeen Teron kopio kuvittelee voivansa ajella Teron autolla 6

Syvä kopiointi on hyvä kopiointi? Tehdään kopio myös olion tilaan kuuluvasta ulkopuolisesta datasta oikea kopiointitapa Mitkä osat olion ulkopuolisista asioista kuuluvat oliolle itselleen ja mitkä ovat rekisteröintejä tai jaettuja resursseja? Mitä olio omistaa ja mitä se käyttää? Kääntäjä ei voi päätellä näitä automaattisesti koodarin on määriteltävä nämä itse Class Circle private: int radius_; Color bordercolor_; Canvas* canvas_; Database* database_; Coordinate* location_; ; 7

Syväkopiointiesimerkki Ennen: Jälkeen: Tero:CarOwner Tero f1() f2() f3() Tero:CarOwner Tero f1() f2() f3() Tero2:CarOwner Tero f1() f2() f3() external data (Lada:Car) external data (Lada:Car) external data (Lada2:Car) Nyt Terolla ja kloonilla on molemmilla oma autonsa! 8

Periytyminen ja kopiorakentaja Kopiorakentaja on rakentaja (OMG?!?!½!) Aliluokan kopiorakentajan on kutsuttava kantaluokan kopiorakentajaa! Kantaluokan kopiorakentaja huolehtii kantaluokan kopiointiosuudesta, aliluokka omastaan Parametrina saatu olio annetaan kantaluokan kopiorakentajan parametriksi Miksi toimii/onnistuu? 9

Kopiorakentajat class Ihminen public: Ihminen( string n ); Ihminen( const Ihminen& i ); virtual ~Ihminen(); ; class Rallikuski: public Ihminen public: Rallikuski( string n, string a ); Rallikuski( const Rallikuski& r ); ; 10

toteutukset Ihminen::Ihminen( string n ): nimi_( n ) cout << "Ihminen " << nimi_ << " syntyi." << endl; Ihminen::Ihminen( const Ihminen& i ): nimi_( "klooni_" + i.nimi_ ) Ihminen::~Ihminen() cout << "Ihminen kuoleentui: " << nimi_ << endl; Rallikuski::Rallikuski( string n, string a ): Ihminen( n ), auto_( a ) Rallikuski::Rallikuski( const Rallikuski& r ): Ihminen( r ), auto_( r.auto_ ) cout << "rallikuskin kloonaus." << endl; 11

Mitäs täällä tapahtuukaan? Rallikuski-esimerkki laajennettuna (Rallikuski, Formulakuski ja SupKuski perittynä Ihmisestä) void haastattele( Ihminen kohde ) kohde.haastattelu(); Ihminen annaaakkosissaeka( Ihminen& vertailtava1, Ihminen& vertailtava2 ) return vertailtava1.nimi() >vertailtava2.nimi()? vertailtava1: vertailtava2; vector< Ihminen > ihmiskasa; Rallikuski mikko( "Mikko", "Ford" ); ihmiskasa.push_back( mikko ); ihmiskasa.push_back( SupKuski( "Yontza" ) ); for( Ihminen ihminen: ihmiskasa ) ihminen.haastattelu(); 12

Viipaloituminen C++:ssa oliota luotaessa on tiedettävä olion tyyppi (koodari kertoo, mitä haluaa luoda) Entäs jos halutaan kopioida kantaluokkaosoittimen päässä olevat oliot? Aliluokan olio on myös kantaluokan olio Mikä tahansa jälkeläinen kelpaa hyvin myös kantaluokan kopiorakentajalle Luodaan uusi kantaluokan olio kopiona aliluokan oliosta == viipaloituminen (slicing) 13

viipaloituminen Cat misse( "Misse" ); Animal elukka( misse ); Misse kelpaa hienosti kopiorakentajan parametriksi, mutta kopiointi koskee vain kantaluokan osuutta Aliluokan osuuteen ei kosketa ja se jää kopioitumatta => viipaloituminen DWidget Kantaluokan kopiorakentaja DWidget Rectangle Viipaloituminen! Going to be sliced 14

Viipaloitumispaikkoja Viipaloituminen voi tapahtua vahingossa(kin) useissa eri tilanteissa KirjastonKirja etsimyohassaoleva( const vector< KirjastonKirja* >& kirjat ); void kirjani( Kirja munkirja ) cout << Tää on mun kirja!" << endl; munkirja.tulosta( cout ); Paluuarvot ja funktion parametrit Kirjat klassikko("donkey Hot", "Mikuel de Servettes" ); klassikko = etsimyohassaoleva( kirjat ); KirjastonKirja lainattu( "Volter Kilpi", "Alastalon salissa", Date(1,1,2011 ) ); kirjani( lainattu ); vector< Ihminen > ihmiskasa; ihmiskasa.push_back( mikko ); ihmiskasa.push_back( SupKuski( "Yontza" ) ); Stl:n säiliöt Kun käsitellään olioita, joilla voi olla aliluokkia/perintähierarkioita, kannattaa vektoriin tuupata vain osoittimia 15

Viipaloituminen ja muut oliokielet Kopioimisviipaloituminen lähes yksinomaan C++:n ongelma Useimmissa muissa kielissä oliot aina viitteiden päässä olioiden elinkaari aina dynaaminen olio kopioidaan käskemällä alkuperäistä oliota kopioimaan itsensä Olio itse tietää oikean tyyppinsä, joten kopiointi onnistuu Arvoparametrit ja paluuarvot harvinaisia muissa kielissä (paitsi perustietotyypit) Muissa kielissä tietorakenteissa vain viitteitä olioihin 16

Kloonien tekeminen Miten kopioida kantaluokan päässä oleva olio, jonka tyyppi voi olla mitä vaan? Käydään läpi kaikki vaihtoehdot tyyppitarkastuksilla, kutsutaan oikeaa kopiorakentajaa kun tyyppi selvillä, ei ehkä kätevin vaihtoehto Entä jos/kun hierarkiaa laajennetaan/muokataan? 17

Kloonien tekeminen, kloonit Viipaloituminen kierrettävissä C++:ssa matkimalla muita kieliä Luokkiin jäsenfunktio kloonaa: Luo new:llä kopion oliosta(kopiorakentajaa käyttäen) Palauttaa osoittimen uuteen olioon Virtuaalifunktio, määritellään uudelleen jokaiseen aliluokkaan Kutsuttaessa luo aina oikeantyyppisen kopion olioista Ongelmia: Kopion tuhoamisvastuu siirtyy kutsujalle (delete) tai fiksujen osoittimien käyttö Joka luokkaan muistettava kirjoittaa oma kloonausfunktio, mitä käy jos unohdetaan? Muuta: (Paluuarvo-osoittimen tyyppi vaihtuu aliluokassa kovariantit paluutyypit) 18

Java ja kloonailu Javassa kloonattavissa olevat oliot tukevat clone() funktiota Tehdään kertomalla, että tietotyyppi toteuttaa Cloneable rajapinnan (erikoisrajapinta) Jos toteutusta ei määritellä, tekee vakiona matalakopioinnin (täälläkin koodari joutuu erikseen määrittelemään, jos halutaan syvä kopio) Javassakaan kopiointi/kloonaus ei ole välttämättä ongelmaton public class Viesti implements Cloneable private String sisalto; private Paivays pvm; public Viesti( String animi, Paivays apvm ) this.nimi = animi; this.pvm = apvm; public Object clone() throws CloneNotSupportedException Viesti clone = (Viesti)super.clone(); // kopioidaan myös viitteen päässä oleva Paivays-olio // (myös päiväyksen on oltava Cloneable) clone.pvm = (Paivays)pvm.clone(); return clone; 19

Periytyminen ja sijoitusoperaattori Sijoituksessa sama vastuujako kuin kopioinnissa: Aliluokan sijoitusoperaattori: kutsuu kantaluokan sijoitusta, sijoittaa aliluokkaosan Kantaluokan sijoitusoperaattori: sijoittaa kantaluokkaosan Aliluokan sijoitusoperaattorissa muistettava kutsua kantaluokan sijoitusoperaattoria! Kääntäjä ei varoita, jos kutsu unohtuu! (ja kantaluokan osa jää sijoittamatta) 20

Sijoitusoperaattorit class Organism public: virtual ~Organism(); virtual Organism* clone() const = 0; ; class Animal: public Organism public: Animal( std::string id ); Animal( const Animal& a ); Animal& operator =( const Animal& ); virtual ~Animal(); virtual void move( Location newl ) = 0; virtual std::string getid() const; Location getlocation() const; private: Location loc_; std::string id_; ; class Bird: public Animal public: Bird( std::string id ); Bird( const Bird& ); Bird& operator =( const Bird& ); virtual ~Bird(); virtual void sing() const; virtual void move( Location newl ) = 0; virtual void printvisited() const; private: std::vector< Location* > visited_; ; class Crow: public Bird public: Crow( std::string id ); Crow( const Crow& c ); Crow& operator =( const Crow& ); virtual ~Crow(); virtual void sing() const; virtual void move( Location newl ); ; class CarrionCrow: public Crow public: CarrionCrow( std::string id ); CarrionCrow( const CarrionCrow& cc ); CarrionCrow& operator =( const CarrionCrow& ); virtual ~CarrionCrow(); virtual void sing() const; ; 21

Toteutukset (Animal & Bird) Animal& Animal::operator =( const Animal& a ) if( this!= &a ) // kantaluokkakutsu varmuuden vuoksi, jos joskus laajennetaan Organism::operator =( a ); id_ = a.id_; loc_ = a.loc_; return *this; Bird& Bird::operator =( const Bird& b ) if( this!= &b ) Animal::operator =( b ); // tuhotaan vanhat while(!visited_.empty() ) delete visited_.at( visited_.size()-1 ); visited_.pop_back(); // kopioidaan kaikki sijoituskohteesta for( unsigned i(0); i < b.visited_.size(); ++i ) Location* loc = b.visited.at( i ); Location* newloc = new Location( *loc ); visited_.push_back( newloc ); return *this; 22

Toteutukset (Crow, CarrionCrow) Crow& Crow::operator =( const Crow& c ) if( this!= &c ) Bird::operator =( c ); return *this; CarrionCrow& CarrionCrow::operator =( const CarrionCrow& c ) if( this!= &c ) Crow::operator =( c ); return *this; 23

Viipaloituminen ja sijoitus Viipaloituminen vaarana myös sijoituksessa Aliluokan olio on kantaluokan olio sen voi sijoittaa kantaluokan olioon Kantaluokkaviitteiden ja -osoittimien kautta mahdollista myös aliluokkaolion sijoittaminen toisen aliluokan olioon! Sijoituksen viipaloituminen vaarana myös muissa oliokielissä (mutta niissä ei yleensä kieleen sisäänrakennettua oliosijoitusta) 24

class NinjaTurtle: public Animal public: NinjaTurtle(std::string id,std::string secretpower); NinjaTurtle( const NinjaTurtle& ); NinjaTurtle& operator =( const NinjaTurtle& ); void usepower() const; virtual std::string getid() const; virtual void move( Location newl ); NinjaTurtle* clone() const; private: std::string secretpower_; ; void assign( Animal& towhere, const Animal& from ) cout << "Before the assignment: " << endl; towhere.getinfo(); towhere = from; cout << "After the assignment: " << endl; towhere.getinfo(); int main() CarrionCrow cr( "Kalle" ); CarrionCrow* cr2 = cr->clone(); // kloonaamalla kopiointi NinjaTurtle raphael( "Raphael", "eat pizza" ); NinjaTurtle donatello( "Donatello", "consume pizza" ); assign( raphael, donatello ); assign( cr, *cr2 ); assign( donatello, cr ); Animal& donatelloref = donatello; donatelloref = cr; cr->printvisited(); delete cr2; // muistetaan poistaa dynaamisen kopioinnin luomat oliot return EXIT_SUCCESS; 25

Esimerkki sijoituksesta (elaintason viitteet) Korpista korppiin Korppi->ninjakilpikonna Elain Elain Elain Korppi viipaloituminen Korppi viipaloituminenninjakilpikonna loc1 loc2 loc3 loc4 Salainen kyky 26

Sijoitusviipaloitumisen estäminen Sijoitusoperaattori ei voi olla virtuaalinen: parametri ei ole sama aliluokassa! Muutamia(enemmän tai vähemmän) toimivia ratkaisuja: Estetään sijoitus, ei tuu viipaloitumisia Kaikki kantaluokat abstrakteja ei kantaluokkaolioita (sijoituksen voi silti tehdä, jos perityissä luokissa on määritelty sijoitusoperaattorit eikä niissä kutsuta kantaluokan sijoituksia) Sijoitettavan tyypin tarkastaminen ajoaikana typeid-operaattorilla 27

1 #include <typeinfo> 2 Korppi& Korppi::operator =(Korppi const& m) 3 4 if(typeid(*this)!= typeid(korppi) typeid(m)!= typeid(korppi)) 5 6 /* Virhetoiminta, esim. Poikkeus */ 7 8 // Tai assert(typeid(*this)== typeid(korppi) && typeid(m) == 9 // typeid(korppi)); 10 if(this!= &m) 11... // normaali sijoitus 12 28

Kloonaaminen ja kloonattavuuden tarkistus Miksi kloonaus toimii, mutta virtuaalinen sijoitusoperaattori ei? Paluuarvo-osoittimen tyyppi vaihtuu aliluokassa kovariantit paluutyypit Sama ei sallittua funktion parametreille 29

Yhteenvetoa Kopiointi ja sijoittaminen muuttuvat haasteellisemmiksi perinnän astuessa mukaan kuvioihin Viipaloituminen (kopio, sijoitus) Dynaaminen kloonaaminen toimii, jos olion tyyppistä ei ole tietoa 30