C++ veya Java gibi diller öğretilirken genellikle "is-a" ilişkisinin "has-a" ilişkisinden daha iyi olduğu söylenir. Yani, bir sınıfın içinde başka bir sınıftan nesne tutmak yerine aynı sorunu türeterek çözmemizin daha iyi olduğu söylenir. Hatta cümle olarak "Favor inheritance over composition", yani "türetmeyi kompozisyona (sahibi olma ilişkisi) tercih et". Fakat aslında bu genellikle tam tersidir. Genellikle diyorum çünkü gerçek hayatta amaca yönelik olarak her şey değiştirilebilir. Mesela çok hızlı çalışmasını düşündüğümüz bir robotik projesinde chip üzerinde (Eşref Adalı görse çok kızardı) Java veya C# kodunun çalışmasını henüz beklemeyiz. Dolayısıyla tasarım şablonları bir amaç değil araçtır.
"Kompozisyonu türetmeye tercih et!"
Bu bölümde tam da bu sözü doğrulayan bir tasarım şablonu olan strateji şablonunu anlatacağım. Strateji şablonu aralarında değiştirilebilir algoritma aileleri tanımlar. Ayrıca strateji algoritmanın kullanıcısından bağımsız olarak değişmesini sağlar.
Strateji şablonunu kullanarak her davranış değişikliği için bu davranışı kullanan sınıfı türetmek yerine bu davranışı temsil eden bir arayüz nesnesi
tutup ondan davranışlar türetilirse kullanıcı olan sınıf artık yeni davranışları takip etmek zorunda kalmaz.
Bunu anlatmak için örnek verelim. Örnekleri Java dilinde vereceğim çünkü bahsettiğim gibi tasarım şablonları ile en iyi uyumlu olan dil Javadır.
Örneğin bize kendini tanıtan, bir yere giden ve bize veda edip geldiği yere geri dönen bir Person sınıfımız olsun. Bu kişi farklı dillerde selam verebilir, selamlaşabilir ve bir yere farklı şekillerde gidebilir. Bütün bu çeşitler için bir sınıf türetmeye kalksak tüm davranışların sayısının çarpımı kadar alt sınıf yazmamız gerekirken, bazı sınıflar ise belki çok anlamsız ve gereksiz olacaktır. Ayrıca alt sınıfımızı dinamik olarak değiştirmek de güvenli olmayabilir.
//package info public class Person { //constructors public void introduce() { helloBehavior.sayHello(this); } public void moveTo(int x, int y) { moveBehavior.move(this, x, y); } public void leaveAndGoTo(int x, int y) { goodbyeBehavior.sayGoodbye(); moveBehavior.move(this, x, y); } // Getters and setters // Behaviors HelloBehavior helloBehavior; GoodbyeBehavior goodbyeBehavior; MoveBehavior moveBehavior; // Properties protected int x, y; protected String name; }
Burada anlattığım gibi üç tane davranışı temsil eden HelloBehavior, GoodbyeBehavior,
MoveBehavior arayüzleri var. Burada ayrıca bir prensibi daha görüyoruz:
"Uygulamayı değil, arayüzü tasarla!"
MoveBehavior arayüzleri var. Burada ayrıca bir prensibi daha görüyoruz:
"Uygulamayı değil, arayüzü tasarla!"