SOLID
SOLID սկզբունքները ծրագրավորման մեջ
SOLID-ը ծրագրավորման սկզբունքների հավաքածու է, որը մշակվել է Ռոբերտ Մարտինի (Uncle Bob) կողմից։ Այս սկզբունքները օգնում են ստեղծել մաքուր, ընդլայնելի և հեշտ պահպանվող կոդ։
Յուրաքանչյուր դաս պետք է ունենա միայն մեկ փոփոխության պատճառ։ Այսինքն՝ այն պետք է պատասխանատու լինի միայն մեկ ֆունկցիոնալության համար։
Վատ օրինակ:
class Report {
generateReport() {
// հաշվետվության ստեղծման լոգիկա
}
printReport() {
// տպման լոգիկա
}
saveToFile() {
// ֆայլի պահպանում
}
}
Այս կոդում Report դասը միաժամանակ զբաղվում է հաշվետվության ստեղծմամբ, տպմամբ և պահպանումով, ինչը խախտում է SRP-ն։
Լավ օրինակ:
class ReportGenerator {
generate() {
// հաշվետվության ստեղծման լոգիկա
}
}
class ReportPrinter {
print() {
// տպման լոգիկա
}
}
class ReportSaver {
saveToFile() {
// ֆայլի պահպանում
}
}
Այստեղ յուրաքանչյուր դաս պատասխանատու է միայն մեկ գործողության համար՝ կոդը դարձնելով ավելի ընթեռնելի և կառավարելի։
Ծրագրային կոդը պետք է փակ լինի փոփոխությունների, բայց բաց՝ ընդլայնման համար։
Վատ օրինակ:
class Payment {
process(type: string) {
if (type === "credit") {
// վարկային քարտի վճարում
} else if (type === "paypal") {
// PayPal վճարում
} else if (type === "crypto") {
// կրիպտո վճարում
}
}
}
Խնդիրը նրանում է, որ եթե ցանկանում ենք ավելացնել նոր վճարման տարբերակ, ստիպված ենք փոփոխել Payment դասը։
Լավ օրինակ:
interface PaymentMethod {
process(): void;
}
class CreditCardPayment implements PaymentMethod {
process() {
console.log("Վճարում վարկային քարտով");
}
}
class PayPalPayment implements PaymentMethod {
process() {
console.log("Վճարում PayPal-ով");
}
}
class CryptoPayment implements PaymentMethod {
process() {
console.log("Վճարում կրիպտոով");
}
}
class PaymentProcessor {
constructor(private paymentMethod: PaymentMethod) {}
processPayment() {
this.paymentMethod.process();
}
}
Այստեղ, եթե ցանկանում ենք ավելացնել նոր վճարման մեթոդ, պարզապես ստեղծում ենք նոր դաս PaymentMethod ինտերֆեյսից՝ առանց փոփոխելու գոյություն ունեցող կոդը։
Ենթադասերը պետք է կարողանան փոխարինել իրենց ծնող դասին՝ առանց փոփոխության անհրաժեշտության։
Վատ օրինակ:
class Bird {
fly() {
console.log("Թռչում է");
}
}
class Penguin extends Bird {
fly() {
throw new Error("Պինգվինները չեն կարող թռչել");
}
}
Այս օրինակում Penguin դասը խախտում է LSP-ն, քանի որ չի կարող իրեն պահել ինչպես ծնող Bird դասը։
Լավ օրինակ:
interface Bird { move(): void; } class FlyingBird implements Bird { move() { console.log("Թռչում է"); } } class NonFlyingBird implements Bird { move() { console.log("Քայլում է"); } } class Penguin extends NonFlyingBird {}
Այստեղ մենք ստեղծել ենք FlyingBird և NonFlyingBird, ինչը թույլ է տալիս ճիշտ դասակարգել թռչող և չթռչող թռչունները։
Մի՛ ստիպիր դասերին կիրառել այն մեթոդները, որոնք իրենց պետք չեն։
Վատ օրինակ:
interface Worker {
work(): void;
eat(): void;
}
class Robot implements Worker {
work() {
console.log("Աշխատում է");
}
eat() {
throw new Error("Ռոբոտները չեն ուտում");
}
}
Լավ օրինակ:
interface Workable {
work(): void;
}
interface Eatable {
eat(): void;
}
class Human implements Workable, Eatable {
work() {
console.log("Աշխատում է");
}
eat() {
console.log("Ուտում է");
}
}
class Robot implements Workable {
work() {
console.log("Աշխատում է");
}
}
Բարձր մակարդակի մոդուլները չպետք է կախված լինեն ցածր մակարդակի մոդուլներից։ Երկուսն էլ պետք է կախված լինեն աբստրակցիայից։
Վատ օրինակ:
class Database {
save(data: string) {
console.log("Պահպանում է տվյալները MySQL-ում");
}
}
class UserService {
private db = new Database();
saveUser(user: string) {
this.db.save(user);
}
}
Լավ օրինակ:
interface Database {
save(data: string): void;
}
class MySQLDatabase implements Database {
save(data: string) {
console.log("Պահպանում է տվյալները MySQL-ում");
}
}
class MongoDBDatabase implements Database {
save(data: string) {
console.log("Պահպանում է տվյալները MongoDB-ում");
}
}
class UserService {
constructor(private db: Database) {}
saveUser(user: string) {
this.db.save(user);
}
}
Այս սկզբունքները կօգնեն ձեզ գրել ավելի մաքուր, ճկուն և ընդլայնվող կոդ՝ խուսափելով ճարտարապետական խնդիրներից։