Giriş
Composition UML terminolojisinde parent child (parça bütün) ilişkisine sahip ve birbirinden ayrılamaz parçalar anlamına gelir.
Composition UML terminolojisinde parent child (parça bütün) ilişkisine sahip ve birbirinden ayrılamaz parçalar anlamına gelir.
Ancak bu yazıdaki Composition kelimesinden kasıt bu değil. Yani UML terminolojisi ile konuşulmuyor. UML ile konuşuyor olsaydık sanırım başlık "Association over Inheritance" olmalıydı. Zaten Composition tanımına bakınca nüanslara takılmayın deniliyor. Kısaca kasıt bir başka sınıfı Field (Üye Alan) olarak kullanmak
Composition: when a Field’s type is a class, the field will hold a reference to another object, thus creating an association relationship between them. Without getting into the nuances of the difference between simple association, aggregation, and composition, let’s intuitively define composition as when the class uses another object to provide some or all of its functionality.
Kalıtım Ne Zaman Kötü
Bazen nesnenin yaşam döngüsü içinde kendiliğinden değişecek şeyler, bileşim yapılacağına kalıtım haline getiriliyor.
Örnek
Elimizde şöyle bir kod olsun. Burada herkese Person nesnesinden kalıtıyor. Student mezun olup Teacher olursa ne olacak ? Buradaki sıkıntı değişebilecek/geçici bir şeyin yani rollerin kalıtım ile ele alınması
class Person {... }
Teacher : Person {...}
class Student : Person {...}
Kalıtımı Bileşim Haline Getirmek
1. Büyük Sınıfları Composition (Birleşim) Haline Getirmek
Bu yöntemde büyük sınıflardaki bazı kodlar XBehavior sınıfına taşınır ve büyük sınıf artık bu kodu çağırır. Açıklaması şöyle.
Bunu yapabilmek için sınıfa bazı attribute/behavior alanları tanımlamak gerekir. Şöyle yaparız
Yine bir sınıftan kalıtım yerine sınıfa yeni şöyle alanlar ekleriz
Örnek - Uzun Inheritance Zinciri
Elimizde şöyle bir kod olsun.
GoF Template Pattern ile ister istemez bir kalıtım oluşuyor. Elimizde şöyle bir kod olsun. Bu kodda esas algoritma Enemy sınıfında. move() metodunda önce Type 2 move yapılıp yapılamayacağı üst sınıfa soruluyor. Cevaba göre Type 1 ve Typ2 move yapılıyor. Ancak Type 3 move de dahil edilmek istenirse işler karışmaya başlıyor.
Multiple inheritance single responsibility kuralını ihlal ediyor gibi görünüyor. Refactoring yaklaşımında multiple inheritance, composition (bileşim) ile değiştiriliyor. Böylece sınıfın tek bir iş yapması sağlanıyor.
Aşağıda multiple inheritance yerine composition getirilmesini gösteren bir örnek var. Sprite sınıfı ilk başta hem Node hem de kaynakları otomatik olarak bırakan Resource sınıfından türüyor.
Composition haline getirmek için önce iki tane arayüz tanımlanıyor.
Elimizde şöyle bir kod olsun.
Composition Over Inheritance Functional Yöntemler yazısına taşıdım
Bu yöntemde büyük sınıflardaki bazı kodlar XBehavior sınıfına taşınır ve büyük sınıf artık bu kodu çağırır. Açıklaması şöyle.
In object-oriented programming, we can use composition in cases where one object "has" (or is part of) another object. Some examples would be:Örnek
- A car has a battery (a battery is part of a car).
- A person has a heart (a heart is part of a person).
- A house has a living room (a living room is part of a house).
Bunu yapabilmek için sınıfa bazı attribute/behavior alanları tanımlamak gerekir. Şöyle yaparız
class Entity
movable
harmable
burnable
freezable
...
Daha sonra farklı özelliklere sahip nesneler tanımlarız. Şöyle yaparızdrunkard = Entity(
movable=SometimesRandomMovable(),
harmable=BasicHarmable(),
burnable=MonsterBurnable(),
freezable=LoseATurnFreezable()
...
)
Şöyle yaparızninja = Entity(
movable=QuickMovable(),
harmable=WeakHarmable(),
burnable=MonsterBurnable(),
freezable=NotFreezable()
...
)
ÖrnekYine bir sınıftan kalıtım yerine sınıfa yeni şöyle alanlar ekleriz
public class Person {
public MarriageStatus marriageStatus;
public Race race;
public Wealth wealth;
}
Yeni alan sınıflarını tanımlarız. Şöyle yaparızpublic class MarriageStatus {
public Datetime anniversary;
public Person husband;
public Person wife;
// TODO: In the future the stakeholder would like to support polyamory
// public List<Person> spouses;
}
2. Kalıtımı (Inheritance) Kırarak, Birleşim (Composition) Haline GetirmekÖrnek - Uzun Inheritance Zinciri
Elimizde şöyle bir kod olsun.
public class NamedEntity
{
public int Id { get; set; }
public string Name { get; set; }
}
Ondan kalıtan bir sınıf daha olsunpublic class AuditedEntity : NamedEntity
{
public DateTime CreatedOn { get; set; }
public DateTime UpdatedOn { get; set; }
}
Sınıfımız şöyle olsunpublic class Three : AuditedEntity
{ }
Bu iki seviye kalıtımı kırmak için iki interface haline getiririz. Şöyle yaparız. Burada yine tam anlamıyla kalıtımdan kaçınılmıyor ancak en azından iki seviye kalıtım yok.public interface INamedEntity
{
int Id { get; set; }
string Name { get; set; }
}
public interface IAuditedEntity
{
DateTime CreatedOn { get; set; }
DateTime UpdatedOn { get; set; }
}
public class One
{ }
public class Two : INamedEntity
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Three : INamedEntity, IAuditedEntity
{
public int Id { get; set; }
public string Name { get; set; }
DateTime CreatedOn { get; set; }
DateTime UpdatedOn { get; set; }
}
public class Four : IAuditedEntity
{
DateTime CreatedOn { get; set; }
DateTime UpdatedOn { get; set; }
}
Örnek - Template PatternGoF Template Pattern ile ister istemez bir kalıtım oluşuyor. Elimizde şöyle bir kod olsun. Bu kodda esas algoritma Enemy sınıfında. move() metodunda önce Type 2 move yapılıp yapılamayacağı üst sınıfa soruluyor. Cevaba göre Type 1 ve Typ2 move yapılıyor. Ancak Type 3 move de dahil edilmek istenirse işler karışmaya başlıyor.
abstract class Enemy:
show() // Called each game tick
update() // Called each game tick
move() // Tries alternateMove, if unsuccessful, perform type 1 movement
abstract alternateMove() // Returns a boolean
class Drunkard extends Enemy:
alternateMove(): return False
class Mummy extends Enemy:
alternateMove() // Type 2 movement if in range, otherwise return false
class Ninja extends Enemy:
alternateMove() // Type 3 movement and return true
Bu durumda basit bir çözüm olarak belki şöyle yaparız. Yani move() abstract yaparız. Template pattern'i MovementPlanEnemy sınıfına taşırız. Template olarak çalışmayan Ninja ise move metodunu override ederek hayatına devam eder. Ancak bu sefer kalıtmın bir kısmı template pattern'i kullanır bir kısmı da kullanmaz hale geliyor.abstract class Enemy:
show() // Called each game tick
update() // Called each game tick
abstract move() // Called in update
class MovementPlanEnemy:
move() // Type 1 movement
abstract alternateMove()
class Drunkard extends MovementPlanEnemy:
alternateMove() // Return false
class Mummy extends MovementPlanEnemy:
alternateMove() // Tries type 2 movement
class Ninja extends Enemy:
move() // Type 3 movement
Bir diğer çözüm ise template pattern'i tamamen bozarak herkesin kendi move() metodunu gerçekleştirmesi. Şöyle yaparız. Her move() metodu ortak kodları yine XBehavior sınıflarına delegate edebilir.abstract class Enemy:
show() // Called each game tick
update() // Called each game tick
abstract move() // Called in update
class Drunkard extends Enemy:
move() // Type 1 movement
class Mummy extends Enemy:
move() // Type 1 + type 2 movement
class Ninja extends Enemy:
move() // Type 3 movement
Örnek - Multiple InheritanceMultiple inheritance single responsibility kuralını ihlal ediyor gibi görünüyor. Refactoring yaklaşımında multiple inheritance, composition (bileşim) ile değiştiriliyor. Böylece sınıfın tek bir iş yapması sağlanıyor.
Aşağıda multiple inheritance yerine composition getirilmesini gösteren bir örnek var. Sprite sınıfı ilk başta hem Node hem de kaynakları otomatik olarak bırakan Resource sınıfından türüyor.
Composition haline getirmek için önce iki tane arayüz tanımlanıyor.
// traditional part
interface Node {
Position getPosition();
// anything else
}
interface Resource {
void allocate(...);
void destroy();
}
Daha sonra bu arayüzleri döndüren iki yeni XHolder arayüzü tanımlanıyor.// composition interfaces
interface NodeHolder {
Node asNode(); // the only method
}
interface ResourceHolder {
Resource asResource(); // the only method
}
Nihayetinde Sprite sınıfı her iki XHolder arayüzünden kalıtıyor.class Sprite
extends Node
implements NodeHolder, ResourceHolder
{
private Resource my_resource;
public Sprite(...) {
// whatever construction needed
my_resource = new ResourceImpl();
}
Örnek - Multiple InheritanceElimizde şöyle bir kod olsun.
class Application implements DatabaseReader, DatabaseWriter, UserInteraction,
Visualizer {
...
}
DatabaseReader ve DatabaseWriter arayüzlerini kalıtımdan kurtarıp compositon yapısına getirmek için şöyle yaparız.interface DatabaseReader { String read(); }
interface DatabaseWriter { void write(String s); }
class Database {
DatabaseConnection connection = create();
DatabaseReader reader = createReader(connection);
DatabaseReader writer = createWriter(connection);
DatabaseReader getReader() { return reader; }
DatabaseReader getWriter() { return writer; }
}
3. Functinal YöntemlerComposition Over Inheritance Functional Yöntemler yazısına taşıdım
Hiç yorum yok:
Yorum Gönder