GoF 2 alapelv
Ezt a szócikket össze kellene dolgozni az Öröklődés helyett objektum-összetétel szócikkel. |
Az 1994-ben a Design Patterns: Elements of Reusable Object-Oriented Software (Programtervezési minták, Újrahasznosítható elemek objektumközpontú programokhoz) c. könyvben jelent meg. A GoF 2 eredeti angol megfogalmazása: „Favor object composition over class inheritance”,[1] azaz „Használj objektum-összetételt öröklés helyett, ha csak lehet”. Az objektum-összetétel az öröklődés (inheritance) alternatívája. Az öröklődést szokás IS-A kapcsolatnak (the dog is a vertebrate / a kutya egy gerinces), míg az objektum összetételt HAS-A kapcsolatnak (the dog has a spine / a kutyának van egy gerince) nevezni. Itt az új szolgáltatások úgy jönnek létre, hogy kisebb részekből építünk fel objektumokat, hogy több szolgáltatással rendelkezzenek. Az objektum-összetételnél az összeépített objektumoknak jól meghatározott interfésszel kell rendelkezniük. Az ilyen újrafelhasználást feketedobozos újrafelhasználásnak nevezzük, mert az objektumok belső részei láthatatlanok. Az objektumok „fekete dobozokként” jelennek meg. Az alosztályokon keresztül történő újrafelhasználást fehérdobozos újrafelhasználásnak nevezzük. A „fehér doboz” itt a láthatóságra utal: az öröklődéssel az alosztályok gyakran látják a szülőosztály belső részeit. A GoF 2 felhasználására remek példa a stratégia programtervezési minta.
Az alapelv előnyeit, hátrányait, valamint további információkat megtalálhatja az Öröklődés helyett objektum-összetétel oldalon.
Programozási példák
[szerkesztés]Példa objektum összetételre (GoF 2)
[szerkesztés]C#
[szerkesztés]public interface HogyanRepül
{
void repül();
}
public class LassanRepül : HogyanRepül
{
public void repül()
{
Console.WriteLine("Nagyon lassan repül . . .");
}
}
public class GyorsanRepül : HogyanRepül
{
public void repül()
{
Console.WriteLine("Gyorsan repül...");
}
}
public class Kacsa
{
protected HogyanRepül repülésTípus; // HAS-A kapcsolat
public void repül()
{
// a repülést egy a HogyanRepül interfacet implementáló osztály hajtja végre
repülésTípusa.repül();
}
public HogyanRepül getRepülésTípusa()
{
return repülésTípusa;
}
// Az objektum összetétel egyik legnagyobb előnye, hogy
// futási időben megváltoztatható
public void setHogyanRepül(HogyanRepül repülésTípusa)
{
this.repülésTípusa = repülésTípusa;
}
public void úszik()
{
Console.WriteLine("Úszik a vízen");
}
public void hápog()
{
Console.WriteLine("háp háp");
}
// Az alapértelmezett konstruktorba érdemes beállítani egy
// alapértelmezett osztályt ami implementálja a HogyanRepül interfacet
public Kacsa()
{
repülésTípusa = LassanRepül;
}
// De létrehozhatunk egy külön konstruktort aminek
// az egyik paramétereként megadhatjuk azt.
public Kacsa(HogyanRepül repülésTípusa)
{
this.repülésTípusa = repülésTípusa;
}
}
public class KülönlegesGyorsanRepülőKacsa : Kacsa
{
// Minden tulajdonsága megegyezik az ősével, kivéve a repülés típusa
public KülönlegesGyorsanRepülőKacsa()
{
this.repülésTípusa = new GyorsanRepül();
}
}
Java
[szerkesztés]public interface HogyanRepül{
void repül();
}
public class LassanRepül implements HogyanRepül{
@Override
public void repül() {
System.out.println("Nagyon lassan repül . . .");
}
}
public class GyorsanRepül implements HogyanRepül{
@Override
public void repül() {
System.out.println("Gyorsan repül...");
}
}
public class Kacsa {
HogyanRepül repülésTípusa; // HAS-A kapcsolat
public void repül(){
// a repülést egy, a HogyanRepül interfacet implementáló osztály hajtja végre
repülésTípusa.repül();
}
public HogyanRepül getRepülésTípusa(){
return repülésTípusa;
}
// Az objektum összetétel egyik legnagyobb előnye, hogy
// futási időben megváltoztatható
public void setHogyanRepül(HogyanRepül repülésTípusa){
this.repülésTípusa = repülésTípusa;
}
public void úszik(){
System.out.println("Úszik a vízen");
}
public void hápog(){
System.out.println("háp háp");
}
// Az alapértelmezett konstruktorba érdemes beállítani egy
// alapértelmezett osztályt ami implementálja a HogyanRepül interfacet
public Kacsa(){
repülésTípusa = LassanRepül;
}
// De létrehozhatunk egy külön konstruktort aminek
// az egyik paramétereként megadhatjuk azt.
public Kacsa(HogyanRepül repülésTípusa){
this.repülésTípusa = repülésTípusa;
}
}
public class KülönlegesGyorsanRepülőKacsa extends Kacsa{
// Minden tulajdonsága megegyezik az ősével, kivéve a repülés típusa
public KülönlegesGyorsanRepülőKacsa(){
this.repülésTípusa = new GyorsanRepül();
}
}
Példa öröklésre (GoF 2 ellenpélda)
[szerkesztés]C#
[szerkesztés]public class Kacsa
{
public void hápog()
{
Console.WriteLine("háp háp");
}
public void úszik()
{
Console.WriteLine("úszik a vízen");
}
public void repül()
{
Console.WriteLine("Nagyon lassan repül . . .");
}
}
// Szeretnék egy gyorsan repülő kacsa osztályt létrehozni,
// aminek az összes egyéb tulajdonsága megegyezik a szülőével
public class GyorsKacsa : Kacsa
{
// Ez nem jött be...
// Megöröklöm a szülő osztály repül metódusát
}
//Próbáljuk meg máshogyan
public class KülönlegesGyorsKacsa
{
public void hápog()
{
Console.WriteLine("háp háp");
}
public void úszik()
{
Console.WriteLine("úszik a vízen");
}
// Láthatjuk, hogy emiatt az apró változtatás miatt, kódismétlésre volt szükségünk
// ami egy 100-200 vagy több soros osztálynál már komoly probléma
// Persze megtehetnénk, hogy kiemeljük a Kacsa osztályból azokat a metódusokat,
// amik megegyeznek, de ez hosszú távon nem nyújt megoldást.
// Mi van például akkor ha a következő kacsa típusunk úszik metódusa különbözik?
public void repül()
{
Console.WriteLine("Gyorsan repül...");
}
}
Java
[szerkesztés]public class Kacsa{
public void hápog(){
System.out.println("háp háp");
}
public void úszik(){
System.out.println("úszik a vízen");
}
public void repül(){
System.out.println("Nagyon lassan repül . . .");
}
}
// Szeretnék egy gyorsan repülő kacsa osztályt létrehozni,
// aminek az összes egyéb tulajdonsága megegyezik a szülőével
public class GyorsKacsa extends Kacsa{
// Ez nem jött be...
// Megöröklöm a szülő osztály repül metódusát
}
//Próbáljuk meg máshogyan
public class KülönlegesGyorsKacsa{
public void hápog() {
System.out.println("háp háp");
}
public void úszik(){
System.out.println("úszik a vízen");
}
// Láthatjuk, hogy emiatt az apró változtatás miatt, kódismétlésre volt szükségünk
// ami egy 100-200 vagy több soros osztálynál már komoly probléma
// Persze megtehetnénk, hogy kiemeljük a Kacsa osztályból azokat a metódusokat,
// amik megegyeznek, de ez hosszú távon nem nyújt megoldást.
// Mi van például akkor ha a következő kacsa típusunk úszik metódusa különbözik?
public void repül(){
System.out.println("Gyorsan repül...");
}
}
Kapcsolódó szócikkek
[szerkesztés]- Robert C. Martin
- Programtervezési minták
- Objektumorientált programozás
- Öröklődés helyett objektum összetétel
Jegyzetek
[szerkesztés]- ↑ Design Patterns. Elements of Reusable Object-Oriented Software (1994.11.10.)
Források
[szerkesztés]- Gamma, Helm, Johnson & Vlissides. Design Patterns (könyv). Addison-Wesley (1994). ISBN 0-201-63361-2
- Dr. Kusper Gábor. Programozási technológiák (jegyzet) (2015)
- Kollár Lajos, Sterbinszky Nóra. Programozási technológiák (jegyzet) (2014)