Peruskäsitteitä

Perusteellisestakin suunnittelusta huolimatta olio-ohjelmoinnissa tulee vastaan tilanteita, jolloin alkuperäisten luokkien ominaisuudet ja toiminnot ovat puutteellisia. Luokan laajentaminen onnistuu kolmella tavalla:
-VE1: Alkuperäisen luokan muuttaminen
-VE2: Perintä
peritään alkuperäisestä luokasta uusi luokka joka sisältää vanhojen ominaisuuksien lisäksi uudet tarvittavat ominaisuudet
-VE3: Koostaminen, jolloin luokka joko
- käyttää toisen luokan ominaisuuksia ja toimintoja tai
- luokkaa laajennetaan siten, että se sisältää toisen luokan olion jäsenmuuttujana

Koostaminen

Koostamisessa luokan sisällä käytetään toisen luokan olioita. Koostamiseen liittyy useita olio-ohjelmointiin liittyviä käsitteitä riippuen siitä minkä tasoisesta koostamisesta on kyse.

Alla olevassa esimerkissä on esitetty koostamiseen liittyvät tärkeimmät käsitteet: Assosiation, Aggregation ja Composition.

Assosiation

Toinen luokka "käyttää" toisen luokan olioita.
- opettajalla on henkilökortti tai opettaja käyttää henkilökorttia
- "luokkahuone käyttää opettajia ja oppilaita" ei kuulosta hyvältä mutta "luokkahuoneessa on yksi opettaja ja oppilaita" kuulostaa jo paljon paremmalta

Aggregation

Toinen luokka "sisältää" toisen luokan olioita.
- luokkahuone sisältää tietokoneita
- osasto sisältää opettajia

Composition

Toinen luokka "sisältää" toisen luokan olioita, jos käyttävä luokka poistuu, poistuu myös käytetty.
- rakennus sisältää luokkahuoneita
- JAMK sisältää osastoja

Hieman esimerkkiä yllä olevasta ohjelmointikoodina

Alla on esitettynä muutama luokka havainnollistamaan luokkarakenteita ja yhteyksiä yllä esitetystä UML-kaaviosta:

Student-luokka periytyy Person-luokasta ja toteuttaa ILearning-rajapinnan

public class Student : Person, ILearning
{
	public string StudentID { get; set; }
	public string GroupID { get; set; }
    public void Read {    
    }
    public void Write {    
    }    
}

Classroom-luokka koostaa sisälleen Student-luokan oliota, Computer-luokan olioita, Teacher-luokan olio sekä sisältää oman RoomNumber-ominaisuuden:

public class Classroom
{
	public List<Student> Students { get; set; }
	public List<Computer> Computers { get; set; }
	public Teacher CurrentTeacher { get; set; }
	public string RoomNumber { get; set; }
}

Perintä vai koostaminen?

Esimerkkitoteutus: Association

/* Association:
	 User uses Luser, but Luser has his own life
*/

using System;

public class User {
	~User(){
		Console.WriteLine("User is destructed");
	}

	public void PrintName (Luser luser) {
	Console.WriteLine("User uses luser " + luser.Name);
	}
};

public class Luser {
	public string Name = "Arska";
	~Luser(){
		Console.WriteLine("Luser " + this.Name + " is destructed");
	}
};

class Program {
    static void Main(string[] args) {
      Luser luser = new Luser();
      User user = new User();
      user.PrintName(luser);
      user = null;

      //Pakotetaan Garbage Collector tyhjäämään muistinsa
      //joten kutsutaan Userin destruktoria (muuten GC tyhjää muistinsa
      // vasta sitten kun katsoo itse sen tarpeelliseksi eli tällaisissa
      // pienissä ohjelmissa vasta ohjelman lopuksi)
      GC.Collect(); // User is destructed
      Console.ReadLine();
      // luser is destructed
    }
}

Esimerkkitoteutus: Aggregation

/* Aggregation is a special type of association
	 User owns Luser. Luser can't belong to another parent object
	 When User dies, Luser may die ("Yuri") or live on ("Arska")
*/

using System;

public class User {
	private Luser _luser;

	public User () {
	}

	public User (Luser luser) {
		this._luser = luser;
	}

	~User(){
		Console.WriteLine("User is destructed");
	}

	public void PrintName() {
		Console.WriteLine("User uses luser " + _luser._name);
	}

};

public class Luser {
	public string Name;

	public Luser (string name) {
		this.Name = name;
	}

	~Luser(){
		Console.WriteLine("Luser " + this.Name + " is destructed");
	}
};

class Program {
    static void Main(string[] args) {
        Luser luser = new Luser("Arska");
        User user1 = new User(luser);
        User user2 = new User(new Luser("Yuri"));
        user1 = null;
        user2 = null;
        GC.Collect(); //  User is destructed,  User is destructed, User Yuri is destructed
        Console.ReadLine();
        // Luser Arska is destructed
    }
}

Esimerkkitoteutus: Composition

/* Composition is a special type of association
	 User owns Luser. Luser can't live on without User
	 When User dies, Luser dies too
*/

using System;

public class User {
	private Luser luser = new Luser();

	public User () {
		luser.Name = "Arska";
	}

	~User(){
		Console.WriteLine("User " + this.luser.Name +" is destructed");
	}

	public void PrintName() {
		Console.WriteLine("Users luser name is " + this.luser.Name);
	}

};

public class Luser {
	public string Name;
	~Luser(){
		Console.WriteLine("Luser is destructed");
	}
};

class Program {
    static void Main(string[] args) {
	User user = new User();
      	user.PrintName(); // Users luser name is Arska
	user = null;

	GC.Collect(); // User Arska is destructed, Luser is destructed
	Console.ReadLine();
    }
}

Yhteenveto

Valinta koostamisen ja perinnän välillä on vaikeasti havaittavissa. Yleisesti vertailussa käytetään "is-a"- ja "has-a"-sääntöä. Jos voidaan ajatella, että LuokkaA on LuokkaB (is-a), niin silloin kannattaa harkita perintää. Jos taas voidaan ajatella, että Luokassa A on toinen luokka B, niin silloin kannattaa koostaa.

Esimerkkejä:
grade "Auto on Moottori", ei kuulosta hyvältä -> ei peritä
grade "Autossa on Moottori", kuulostaa hyvältä -> koostetaan
grade "Opettaja on Henkilö", kuulostaa hyvältä -> peritään
grade "Opettajassa on Henkilö", ei kuulosta hyvältä -> ei koosteta

Lisätietoa:
UML Class Diagrams: Reference
UML Class Diagrams: Guidelines