Писането на метод/функция по добър начин е от решаващо значение за постигане на чист и стабилен код. Въпреки това, понякога е също толкова важно да се реши дали конкретният сегмент от код трябва да лежи като метод или вместо това трябва да бъде клас. В тази публикация ще обсъдя някои от основите, които всеки разработчик трябва да вземе предвид, докато проектира класове.

Принцип на единна отговорност

Означава ли да изпълнявате една задача като във функция? Може да не е, защото един клас може да има повече от един метод и всеки метод изпълнява своя собствена задача. Следователно по-скоро можем да кажем, че акцентира върху писането на клас по такъв начин, че причината за промяна на този клас не трябва да бъде повече от една. Или с други думи, трябва да съберем аспектите на едно и също място, които се променят по една и съща причина и да разделим тези, които се променят по различни причини.

Например:

Има система за извършване на поръчки за материали в ресторант и може да има следните изисквания:

а. Създайте ред

b. Промяна на реда

° С. Премахване на поръчката

д. Показване на преглед на поръчката

д. Прилагане на отстъпка

f. Изпратете имейл за потвърждение

ж. Ред за печат

ч. Показване на отчет за историята на поръчките и др.

Нека пренебрегнем други изисквания (неспоменати по-горе) и да се съсредоточим само върху гореспоменатите изисквания. Какъв би могъл да бъде възможният дизайн на класа за включване на изискванията? Можем да започнем от прост клас, наречен Поръчкаи да го преработим, за да получим по-добър дизайн. Следват възможните методи в класа Поръчка, който използва няколко интерфейса на зависимост:

CreateOrder consumes IOrderRepository
UpdateOrder consumes IOrderRepository
DeleteOrder consumes IOrderRepository
DisplayOrder consumes IOrderRepository
MakeDiscount consumes IDiscount
SendEmail consumes IMailer
PrintOrder consumes IPrinter
GenerateReport consumes IReporter

Нека разгледаме причините, които могат да доведат до промени в този клас. Първо, какво ще стане, ако компанията промени правилата за изпращане на имейли? Най-вероятно трябва да направим корекция в метода SendEmail, който използва интерфейса на IMailer. По същия начин промяната във формата за печат и информацията изисква друга актуализация в класа. Не само това, промяната в политиката за отстъпки и промяната във формата на отчетните данни задейства и други промени в допълнение.

От друга страна, ако разгледаме класа от друга перспектива, т.е. зависимости от класове, първите четири метода зависят от един и същ интерфейс (IOrderRepository), а останалите методи имат зависимости, които са различни един от друг .

Следователно гореспоменатите точки (обща причина за промяна и общи зависимости на интерфейса) ни дават представа, че първите четири метода могат да лежат в един и същи клас. По същия начин, останалите методи могат да съществуват в собствените си отделни класове:

class Order implements IOrderRepository{
 CreateOrder(){}
 UpdateOrder(){}
 DeleteOrder(){}
 DisplayOrder(){}
}
class Discount implements IDiscount{
 MakeDiscount(){}
}
class Email implements IMailer{
 SendEmail(){}
}
class Printer implements IPrinter{
 PrintOrder(){}
}
class Reporter implements IReporter{
 GenerateReport(){}
}

Сплотеност

Един клас трябва да има минимален брой променливи на екземпляр. Ако променлива на екземпляр се използва от всеки метод в класа, тогава можем да кажем, че класът е сплотен. И ако всяка от променливите на екземпляра в клас се използва от всеки метод, тогава класът е оптимално сплотен, но това е почти невъзможно. Въпросът обаче е каква ще е ползата от това? Отговорът е, че ако следвате този принцип, ще се опитате да запазите класовете по-независими, като намалите броя на променливите на екземпляра, от които зависи класът. В резултат на това размерът на класа ще бъде малък, компактен и отделен.

Разделете често променящите се аспекти

Опитайте се да идентифицирате аспектите на вашето приложение, които често се променят или имат възможност за промяна. След това отделете тези аспекти от това, което остава постоянно. Ако говорим за създаване на класове, трябва да отделим променящите се аспекти в техните собствени интерфейси.

Абстракция срещу детайли

Докато консумирате отделните аспекти, консумирайте го като концепция, а не като детайли за изпълнение. С други думи, използвайте интерфейсни/абстрактни класове, които представляват концепция и тази концепция може да бъде имплементирана от един или множество класове. Ето как програмираме върху абстракцията, а не върху детайлите. Това улеснява внедряването на промени в бъдеще, тъй като класовете или компонентите, които консумират изходното състояние на даден обект, не се интересуват от подробностите за изпълнението, т.е. те не гледат как е генериран резултатът, а се интересуват само дали е там.

Изискванията продължават да се променят и ние трябва да пишем класове по такъв начин, че минималните промени в нашия код да могат да отговорят на новия обрат на изискването.

Заключение

Благодаря ви, че прочетохте поста ми. Опитах се да обясня някои от основите на дизайна на класа в тази публикация. Моля, напишете коментари, ако имате някакви мисли за него и натиснете бутона за пляскане, ако ви харесва.

Join Medium for $5 per month - Access all of Medium + support me & others