class Painter { private readonly float daysPerHouse; public Painter(float daysPerHouse) { this.daysPerHouse = daysPerHouse; } public float EstimateDaysToPaint(int houses) { return houses * daysPerHouse; } }
class LandOwner { private readonly Painter painter; private readonly int housesCount; public LandOwner(Painter painter, int housesCount) { this.painter = painter; this.housesCount = housesCount; } public void ManageHouses() { float daysToPaint = this.painter.EstimateDaysToPaint(this.housesCount); Console.WriteLine("Painting houses for {0:0.0} day(s).", daysToPaint); } }
class LandOwner { private readonly IEnumerable<Painter> painters; private readonly int housesCount; public LandOwner(IEnumerable<Painter> painters, int housesCount) { this.painters = new List<Painter>(painters); this.housesCount = housesCount; } ... }
اکنون مالک زمین مسئولیت انجام این محاسبه را برعهده گرفته است؛ ولی این پیاده سازی کمی پیچیدهتر میشود:
class LandOwner { private readonly IEnumerable<Painter> painters; private readonly int housesCount; public LandOwner(IEnumerable<Painter> painters, int housesCount) { this.painters = new List<Painter>(painters); this.housesCount = housesCount; } private float GetVelocity(Painter painter) { return painter.EstimateDaysToPaint(1); } private float GetTotalVelocity() { float sum = 0; foreach (Painter painter in this.painters) sum += 1 this.GetVelocity(painter); return sum; } public void ManageHouses() { float daysToPaint = this.GetTotalVelocity() * this.housesCount; Console.WriteLine("Painting houses for {0:0.0} day(s).", daysToPaint); } }
این پیاده سازی کمی پیچیدهاست؛ اما کار میکند و همچنین دارای مشکلاتی است. فرض کنید یکی از نقاشان صاحب شرکت، نقاشی است که نقاشان دیگر را استخدام میکند. حتی بدتر از آن این است که اگر شرکت نقاشی، شرکت دیگری را نیز همراه با نقاشان خود، استخدام کند. مالک زمین با سلسله مراتبی چندسطحی از نقاشان روبرو میشود. برآورد زمان لازم برای نقاشی خانهها در چنین شرایطی برای مالک زمین دشوار خواهد شد.
پیاده سازی Composite
اگر تنها بتوانیم یک اینترفیس عمومی را از یک نقاش، بیرون بکشیم، سازماندهی نقاشها راحتتر میشود:
interface IPainter { float EstimateDaysToPaint(int houses); }
مالک زمین دیگر کاری با مجموعه نقاشها ندارد و در حال حاضر تنها یک نقاش انتزاعی را کنترل میکند:
class LandOwner { private readonly IPainter painter; private readonly int housesCount; public LandOwner(IPainter painter, int housesCount) { this.painter = painter; this.housesCount = housesCount; } public void ManageHouses() { float daysToPaint = this.painter.EstimateDaysToPaint(this.housesCount); Console.WriteLine("Painting houses for {0:0.0} day(s).", daysToPaint); } }
اینبار مالک زمین فقط ارجاعی را به یک نقاش انتزاعی دارد. از سوی دیگر، کلاس نقاش دست نخورده باقی میماند و تنها رابط IPainter را پیاده سازی میکند:
class Painter: IPainter { ... }
حالا میتوانیم نتیجه آن را ببینیم. ما آماده تعریف یک عنصر Composite هستیم که خود و عناصرش، اینترفیس IPainter را پیاده سازی کردهاند.
class PaintingCompany: IPainter { private readonly IEnumerable<IPainter> painters; public PaintingCompany(IEnumerable<IPainter> painters) { this.painters = new List<IPainter>(painters); } private float GetVelocity(Painter painter) { return painter.EstimateDaysToPaint(1); } private float GetTotalVelocity() { float sum = 0; foreach (Painter painter in this.painters) sum += 1 this.GetVelocity(painter); return sum; } public float EstimateDaysToPaint(int houses) { return this.GetTotalVelocity() * houses; } }
این پیاده سازی شرکت نقاشی است. کد کلاس LandOwner قبلی که وظیفه آن کنترل نقاشها بود، به این کلاس منتقل شدهاست. تفاوت این است که شرکت نقاشی اکنون تعدادی نقاش انتزاعی را مدیریت میکند. از انتزاعات میتوان دو حالت را در نظر گرفت: به صورت تک و یا به صورت گروه. این مورد قدرت نوع انتزاعی است در برنامه نویسی شیء گرا که در اینجا خودش را به صورت یک نقاش و یا گروهی از افراد که با هم کار میکنند، نشان میدهد.
نتیجه گیری
در این مقاله ما به یک نمونه از الگوی طراحی Composite پرداختیم. با استفاده از الگوی Composite، شیوهای که کلاسها با مجموعهها برخورد میکنند، بسیار سادهتر شدهاست. کار با مجموعهها، کد را پیچیدهتر کرده و باعث میشود کلاس، کاری بیشتر از مسئولیتهای خود را انجام دهد که ربطی به آن ندارد.