در این قسمت از مهمترین اصول طراحی و توسعه نرم افزار در رابطه با قانون Demeter و یا Law of Demeter که به طور کوتاه به آن LOD نیز می گویند صحبت خواهیم کرد. قانون LOD اغلب تحت عنوان Principle of Least Knowledge و یا قانون حداقل دانش نیز شناخته میشود و در کنار مفهوم Loose Coupling که در قسمت قبل آن را بررسی کردیم مطرح می گردد. به طور ساده قانون LOD به این معناست که فقط با دوستان خود صحبت کن اما با غریبه ها صحبت نکن. اگر بخواهیم این جمله را باز کنیم و برای شما در قالب یک مثال توضیح بدهیم باید بگوییم که مفهوم این جمله این است که reference زدن به کلاس هایی که به طور مستقیم با آنها در ارتباط هستیم مشکلی ندارد؛ اما reference زدن به کلاس هایی که باکلاس مورد نظر ما در ارتباط هستند مشکل ایجاد خواهد کرد. به طور کلی در قالب یک مثال ساده که اصل LOD را نقض می کند، همواره از یک کد ساده به نام “PaperBoy, Customer, and Wallet” و یا اگر بخواهم ترجمه آن را بگویم؛ پسر روزنامه فروش، مشتری و کیف پول صحبت میشود. این مثال بسیار ساده است و کد آن را در قسمت زیر مشاهده می کنید.
[c-sharp]
namespace KeepingSoftwareSoftSamples.OtherPrinciples.LoDViolated {
public class Wallet {
public decimal Cash {
get;
set;
}
}
public class Customer {
public Wallet Wallet {
get;
set;
}
}
public class PaperBoy {
public Customer Customer {
get;
set;
}
public decimal CollectedCash {
get;
set;
}
public void CollectPayment() {
// Arbitrary amount for demo purposes
const decimal amountOwed = 2.45m;
// Violation of LoD occurs here:
// – Customer is a "friend"
// – Wallet is a "stranger"
// – PaperBoy should not be "reaching into"
// a Customer’s Wallet
Customer.Wallet.Cash -= amountOwed;
CollectedCash += amountOwed;
}
}
}
[/c-sharp]
همانطور که در این مثال مشاهده می کنید سه کلاس با نامهای Wallet و Customer و PaperBoy تعریف شده اند و در کلاس Wallet یک پروپرتی از نوع decimal به نام Cash قرار گرفته است که مشخص کننده مقدار پول موجود در کیف پولی است.
در کلاس Customer یک پروپرتی از نوع Wallet و با نام Wallet تعریف شده است. به عبارتی هر مشتری یک کیف پول دارد و در کلاس PaperBoy از یک پروپرتی به اسم Customer و با نوع Customer تعریف شده است. علاوه بر این یک پروپرتی از نوع decimal به نام CollectedCash تعریف شده است که مشخص کننده پولی است که پسر روزنامه فروش تا به اینجای کار جمع آوری کرده است.
در متد CollectPayment کد ساده ای وجود دارد که اصل LOD را نقض میکند. در متد CollectPayment ابتدا یک متغیر به نام amountOwed و از نوع decimal به صورت تصادفی تعریف شده است. خطی که اصل LOD را نقض می کند، کدی است که مربوط به دسترسی پیدا کردن به پروپرتی Wallet از کلاس Customer است. بگذارید کمی توضیح بدهم. برای کلاس PaperBoy پروپرتی Customer به عنوان یک دوست تلقی میشود؛ چرا که به طور مستقیم از آن یک پروپرتی در کلاس PaperBoy تعریف شده است؛ اما برای کلاس PaperBoy کلاس Wallet یک غریبه است؛ چرا که از آن به طور مستقیم یک پروپرتی در کلاس PaperBoy تعریف نشده است. بنابراین اگر PaperBoy بخواهد از طریق پل ارتباطی خود یعنی پروپرتی Customer به Wallet دسترسی پیدا کند، اصل LOD را زیر سوال برده است. در کدی که در متد CollectPayment مشاهده می کنید Customer مورد استفاده قرار گرفته و سپس پروپرتی Wallet آن و پس از آن پروپرتی Cash مربوط به Wallet مورد دسترسی قرار گرفته است. این کد اصل LOD را زیر سوال برده است. حال اگر بخواهیم این موضوع را مرتفع کنیم، میتوانیم یک متد به نام GetPayment را تعریف کنیم. این متد در قالب یک delegate بسیار ساده ایفای نقش میکند و نیاز PaperBoy برای دسترسی پیدا کردن به Wallet را مرتفع می نماید. کد زیر این موضوع را نشان می دهد.
[c-sharp]
public class Customer {
private Wallet Wallet {
get;
set;
}
private LooseChangeJar LooseChange {
get;
set;
}
public decimal GetPayment(decimal amountOwed) {
// Additional "business" logic can be added here
// without needing PaperBoy to change as well
var isEnoughLooseChange = LooseChange.Cash >= amountOwed;
if (isEnoughLooseChange) {
LooseChange.Cash -= amountOwed;
}
else {
// "is there enough in wallet" checking logic omitted
Wallet.Cash -= amountOwed;
}
return amountOwed;
}
}
[/c-sharp]
در این کد در کلاس Customer یک متد با نام GetPayment تعریف شده است که به پروپرتی Wallet و سپس پروپرتی Cash مربوط به آن دسترسی پیدا کرده و عملیات کسر پول مورد نظر را از کیف پولی مشتری انجام میدهد. مقدار پولی که باید از کیف پول کم بشود به عنوان پارامتر ورودی متد GetPayment معرفی گشته است. حال در کلاس PaperBoy و متد CollectPayment آن فقط با استفاده از متد GetPayment که در کلاس Customer تعریف شده است پول مربوط به روزنامه فروخته شده، دریافت میگردد. در این مثال دیگر کلاس PaperBoy به کلاس Wallet دسترسی پیدا نمیکند؛ اما به راحتی میتواند به کلاس Customer به عنوان دوست خود دسترسی پیدا کرده و متد GetPayment آن را صدا بزند. با این تغییر دیگر PaperBoy نیازی ندارد که در رابطه با این موضوع که یک Customer در کجا و یا کدام پروپرتی پول خود را نگه میدارد، اطلاعی داشته باشد. علاوه بر این کلاس Customer می تواند نحوه نگهداری پول خود را از کلاس PaperBoy مخفی کند. به همین دلیل است که در ابتدای این آموزش خدمتتان عرض کردم که اصل LOD با اصل Loose Coupling در ارتباط است. حال یک درهم تنیدگی سست بین کلاس Customer و PaperBoy قرار دارد.
دیدگاهها
0