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
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.
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
Toinen luokka "sisältää" toisen luokan olioita.
- luokkahuone sisältää tietokoneita
- osasto sisältää opettajia
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
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; }
}
/* 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
}
}
/* 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
}
}
/* 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();
}
}
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