برنامه نویسی

توسعه دهندگان گوش می دهند: اگر یک مدل دامنه غنی ندارید، از OOP استفاده نمی کنید

Summarize this content to 400 words in Persian Lang
در دنیای توسعه نرم افزار، اصول برنامه نویسی شی گرا (OOP) و SOLID اغلب به عنوان بهترین روش برای ایجاد سیستم های قابل نگهداری، توسعه پذیر و قوی معرفی می شوند. با این حال، یک جنبه مهم که اغلب نادیده گرفته می شود، زمینه ای است که این اصول واقعاً در آن می درخشند: یک مدل دامنه غنی. بیایید بررسی کنیم که چرا یک مدل دامنه غنی برای استفاده از قدرت کامل اصول OOP و SOLID ضروری است و وقتی به مدل دامنه کم خون رضایت می دهیم چه چیزهایی را از دست می دهیم.

مدل دامنه غنی: زمینی حاصلخیز برای OOP و SOLID

یک مدل دامنه غنی با موجودیت هایی مشخص می شود که هم داده ها و هم رفتار را محصور می کنند. این موجودیت ها صرفاً محفظه های داده نیستند، بلکه شرکت کنندگان فعال در منطق تجاری برنامه هستند. این رویکرد کاملاً با اصول اصلی OOP مطابقت دارد و محیط ایده آلی را برای اعمال اصول SOLID فراهم می کند.

چند شکلی در عمل

در یک مدل دامنه غنی، انواع مختلفی از موجودیت ها می توانند رابط های مشترک را پیاده سازی کنند یا کلاس های پایه مشترک را گسترش دهند در حالی که رفتارهای خاص خود را ارائه می دهند. به عنوان مثال، سیستم پارکینگ را در نظر بگیرید:

public abstract class Vehicle
{
public abstract decimal CalculateParkingFee(int hours);
}

public class Car : Vehicle
{
public override decimal CalculateParkingFee(int hours)
{
return hours * 2.5m; // Car parking fee logic
}
}

public class Motorcycle : Vehicle
{
public override decimal CalculateParkingFee(int hours)
{
return hours * 1.5m; // Motorcycle parking fee logic
}
}

public class Bus : Vehicle
{
public override decimal CalculateParkingFee(int hours)
{
return hours * 5m; // Bus parking fee logic
}
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در اینجا، پلی مورفیسم به انواع خودروهای مختلف اجازه می دهد تا منطق محاسبه هزینه خود را ارائه دهند، انعطاف پذیری را ارتقاء داده و کدهای تکراری را کاهش دهند.

وراثت برای استفاده مجدد از کد

رفتارهای رایج را می توان در کلاس های پایه انتزاع کرد و استفاده مجدد از کد را ترویج کرد. به عنوان مثال، در سیستم نقطه پارکینگ:

public abstract class ParkingSpot
{
public string SpotId { get; set; }
public bool IsOccupied { get; set; }

public abstract void ParkVehicle(Vehicle vehicle);
public abstract void RemoveVehicle();
}

public class CompactSpot : ParkingSpot
{
public override void ParkVehicle(Vehicle vehicle)
{
// Parking logic for compact spot
IsOccupied = true;
}

public override void RemoveVehicle()
{
// Logic to remove vehicle from compact spot
IsOccupied = false;
}
}

public class LargeSpot : ParkingSpot
{
public override void ParkVehicle(Vehicle vehicle)
{
// Parking logic for large spot
IsOccupied = true;
}

public override void RemoveVehicle()
{
// Logic to remove vehicle from large spot
IsOccupied = false;
}
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این طراحی امکان عملکرد مشترک در کلاس پایه را با رفتارهای خاص تعریف شده در زیر کلاس ها فراهم می کند.

اصل جایگزینی لیسکوف (LSP) در عمل

با یک مدل دامنه غنی، می توانیم سلسله مراتب کلاس خود را طوری طراحی کنیم که به LSP پایبند باشد. هر زیر کلاس از Vehicle باید در هر جایی قابل استفاده باشد Vehicle بدون شکستن رفتار سیستم مورد انتظار است. این اصل تضمین می‌کند که سلسله‌مراتب شی ما به خوبی طراحی شده‌اند و به طور مداوم رفتار می‌کنند.

public class ParkingLot
{
private ListVehicle> vehicles = new ListVehicle>();

public void AddVehicle(Vehicle vehicle)
{
vehicles.Add(vehicle);
}

public void CalculateFees()
{
foreach (var vehicle in vehicles)
{
Console.WriteLine($”Parking fee: {vehicle.CalculateParkingFee(3)}”);
}
}
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در این مثال، هر زیر کلاس از Vehicle را می توان به ParkingLot، و مربوط به آنها CalculateParkingFee متدها به درستی فراخوانی خواهند شد.

اصل باز/بسته (OCP) برای توسعه پذیری

یک مدل دامنه غنی به ما اجازه می دهد تا عملکرد را بدون تغییر کد موجود گسترش دهیم. به عنوان مثال، اضافه کردن یک نوع وسیله نقلیه جدید مانند ElectricCar می توان با ایجاد یک زیر کلاس جدید از Vehicle، بدون تغییر منطق اصلی پارکینگ.

public class ElectricCar : Vehicle
{
public override decimal CalculateParkingFee(int hours)
{
return hours * 3m; // Electric car parking fee logic
}
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این سیستم اکنون برای جا دادن گسترش یافته است ElectricCar بدون تغییر انواع خودروهای موجود یا منطق پارکینگ.

ترکیبی برای رفتارهای پیچیده

مدل‌های دامنه غنی اغلب از ترکیب برای ساختن موجودیت‌های پیچیده از موجودات ساده‌تر استفاده می‌کنند. به عنوان مثال، الف ParkingLot موجودیت ممکن است از چندین تشکیل شده باشد Level اشیایی که هر کدام شامل چندین است ParkingSpot اشیاء، امکان طراحی مدولار و انعطاف پذیر را فراهم می کند.

public class Level
{
public int LevelNumber { get; set; }
public ListParkingSpot> Spots { get; set; } = new ListParkingSpot>();
}

public class ParkingLot
{
public ListLevel> Levels { get; set; } = new ListLevel>();
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این ترکیب به ما اجازه می دهد تا پارکینگ ها را با سطوح و نقاط مختلف به طور موثر مدیریت کنیم.

مدل دامنه کم خون: یک فرصت از دست رفته

در مقابل، یک مدل دامنه کم خون شامل موجودیت هایی است که کمی بیشتر از ساختارهای داده هستند و رفتار آنها در کلاس های خدمات جداگانه اجرا می شود. در حالی که این رویکرد می تواند کارساز باشد، بسیاری از مزایایی را که اصول OOP و SOLID ارائه می دهند از دست می دهد.موجودیت ها اساسا دارندگان داده با گیرنده و تنظیم کننده هستند.معایب: استفاده محدود از اصول OOP:وراثت و چند شکلی: کمتر معنادار است زیرا منطق دامنه در جای دیگری قرار دارد.

محدود OOP ماe: در یک مدل کم خون با موجودیت های داده محور، فرصت کمتری برای وراثت و چندشکلی وجود دارد. تمرکز بر روی دستکاری داده ها است، نه رفتار پیچیده.اصول جامد نقض نشده است (اما نه اهرمی): از آنجایی که موجودات کم خون دارای حداقل منطق هستند، نقض اصولی مانند جایگزینی Liskov دشوار است (رفتار زیادی برای جایگزینی وجود ندارد). با این حال، این اصول نیز در این زمینه فایده چندانی ندارند.

چند شکلی محدود

با جدا شدن رفتار از داده ها، فرصت کمتری برای استفاده از چند شکلی وجود دارد. به جای اینکه انواع خودروهای مختلف روش‌های محاسبه کارمزد خود را پیاده‌سازی کنند، ممکن است به یک کلاس خدمات واحد با یک بیانیه سوئیچ بزرگ برای رسیدگی به انواع مختلف برسیم.

public class ParkingFeeService
{
public decimal CalculateFee(Vehicle vehicle, int hours)
{
switch (vehicle)
{
case Car _:
return hours * 2.5m;
case Motorcycle _:
return hours * 1.5m;
case Bus _:
return hours * 5m;
default:
throw new ArgumentException(“Unknown vehicle type”);
}
}
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این رویکرد انعطاف پذیری کمتری دارد و حفظ آن سخت تر است.

کپسولاسیون کاهش یافته

مدل‌های کم خون اغلب حالت داخلی خود را از طریق گیرنده‌ها و ستترها نشان می‌دهند و اصل کپسولاسیون را نقض می‌کنند. این می تواند به منطق تجاری پراکنده و افزایش جفت بین اجزا منجر شود.

public class Vehicle
{
public string LicensePlate { get; set; }
public int HoursParked { get; set; }
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

سپس منطق کسب و کار به صورت خارجی مدیریت می شود و پیچیدگی را افزایش می دهد.

برنامه OCP طبیعی کمتر

بدون رفتار غنی در موجودیت‌ها، گسترش عملکرد اغلب مستلزم اصلاح کلاس‌های سرویس موجود است که اصل باز/بسته را نقض می‌کند.

ترکیب کم استفاده

مدل‌های کم‌خون به جای استفاده از قدرت ترکیب شی برای مدل‌سازی روابط و رفتارهای پیچیده دامنه، بیشتر به کد رویه‌ای در خدمات تکیه می‌کنند.

نتیجه گیری: پذیرش مدل دامنه غنی

در حالی که مدل‌های دامنه کم‌خون می‌توانند برای کاربردهای ساده CRUD کافی باشند، اما وقتی با منطق تجاری پیچیده سروکار دارند، کوتاهی می‌کنند. با پذیرش مدل‌های دامنه غنی، توسعه‌دهندگان می‌توانند پتانسیل کامل اصول OOP و SOLID را باز کنند:

موجودیت ها چیزی فراتر از حامل های داده می شوند. آنها رفتار را در بر می گیرند و واقعاً مفاهیم دامنه را نشان می دهند.
چند شکلی و وراثت را می توان برای ایجاد ساختارهای کد انعطاف پذیر و قابل استفاده مجدد مورد استفاده قرار داد.
اصول SOLID کاربردهای طبیعی پیدا می کند که منجر به سیستم های قابل نگهداری و توسعه پذیرتر می شود.
منطق دامنه پیچیده را می توان به وضوح و شهودی از طریق تعاملات شی بیان کرد.

یک مدل دامنه غنی نه تنها با فلسفه OOP هماهنگی بهتری دارد، بلکه پایه محکمی برای ساختن سیستم های نرم افزاری پیچیده و قابل نگهداری است که می تواند با تغییر نیازهای کسب و کار تکامل یابد.

به یاد داشته باشید، هدف OOP فقط گروه بندی داده ها و توابع با هم نیست، بلکه مدل سازی موثر دامنه مشکل است. با دادن رفتاری که به نهادهایتان شایسته است، فقط کد نمی نویسید. شما در حال ایجاد یک نمایش نرم افزاری از دامنه کسب و کار خود هستید که قدرتمند، انعطاف پذیر و واقعی است.

در دنیای توسعه نرم افزار، اصول برنامه نویسی شی گرا (OOP) و SOLID اغلب به عنوان بهترین روش برای ایجاد سیستم های قابل نگهداری، توسعه پذیر و قوی معرفی می شوند. با این حال، یک جنبه مهم که اغلب نادیده گرفته می شود، زمینه ای است که این اصول واقعاً در آن می درخشند: یک مدل دامنه غنی. بیایید بررسی کنیم که چرا یک مدل دامنه غنی برای استفاده از قدرت کامل اصول OOP و SOLID ضروری است و وقتی به مدل دامنه کم خون رضایت می دهیم چه چیزهایی را از دست می دهیم.

مدل دامنه غنی: زمینی حاصلخیز برای OOP و SOLID

یک مدل دامنه غنی با موجودیت هایی مشخص می شود که هم داده ها و هم رفتار را محصور می کنند. این موجودیت ها صرفاً محفظه های داده نیستند، بلکه شرکت کنندگان فعال در منطق تجاری برنامه هستند. این رویکرد کاملاً با اصول اصلی OOP مطابقت دارد و محیط ایده آلی را برای اعمال اصول SOLID فراهم می کند.

چند شکلی در عمل

در یک مدل دامنه غنی، انواع مختلفی از موجودیت ها می توانند رابط های مشترک را پیاده سازی کنند یا کلاس های پایه مشترک را گسترش دهند در حالی که رفتارهای خاص خود را ارائه می دهند. به عنوان مثال، سیستم پارکینگ را در نظر بگیرید:

public abstract class Vehicle
{
    public abstract decimal CalculateParkingFee(int hours);
}

public class Car : Vehicle
{
    public override decimal CalculateParkingFee(int hours)
    {
        return hours * 2.5m; // Car parking fee logic
    }
}

public class Motorcycle : Vehicle
{
    public override decimal CalculateParkingFee(int hours)
    {
        return hours * 1.5m; // Motorcycle parking fee logic
    }
}

public class Bus : Vehicle
{
    public override decimal CalculateParkingFee(int hours)
    {
        return hours * 5m; // Bus parking fee logic
    }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در اینجا، پلی مورفیسم به انواع خودروهای مختلف اجازه می دهد تا منطق محاسبه هزینه خود را ارائه دهند، انعطاف پذیری را ارتقاء داده و کدهای تکراری را کاهش دهند.

وراثت برای استفاده مجدد از کد

رفتارهای رایج را می توان در کلاس های پایه انتزاع کرد و استفاده مجدد از کد را ترویج کرد. به عنوان مثال، در سیستم نقطه پارکینگ:

public abstract class ParkingSpot
{
    public string SpotId { get; set; }
    public bool IsOccupied { get; set; }

    public abstract void ParkVehicle(Vehicle vehicle);
    public abstract void RemoveVehicle();
}

public class CompactSpot : ParkingSpot
{
    public override void ParkVehicle(Vehicle vehicle)
    {
        // Parking logic for compact spot
        IsOccupied = true;
    }

    public override void RemoveVehicle()
    {
        // Logic to remove vehicle from compact spot
        IsOccupied = false;
    }
}

public class LargeSpot : ParkingSpot
{
    public override void ParkVehicle(Vehicle vehicle)
    {
        // Parking logic for large spot
        IsOccupied = true;
    }

    public override void RemoveVehicle()
    {
        // Logic to remove vehicle from large spot
        IsOccupied = false;
    }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این طراحی امکان عملکرد مشترک در کلاس پایه را با رفتارهای خاص تعریف شده در زیر کلاس ها فراهم می کند.

اصل جایگزینی لیسکوف (LSP) در عمل

با یک مدل دامنه غنی، می توانیم سلسله مراتب کلاس خود را طوری طراحی کنیم که به LSP پایبند باشد. هر زیر کلاس از Vehicle باید در هر جایی قابل استفاده باشد Vehicle بدون شکستن رفتار سیستم مورد انتظار است. این اصل تضمین می‌کند که سلسله‌مراتب شی ما به خوبی طراحی شده‌اند و به طور مداوم رفتار می‌کنند.

public class ParkingLot
{
    private ListVehicle> vehicles = new ListVehicle>();

    public void AddVehicle(Vehicle vehicle)
    {
        vehicles.Add(vehicle);
    }

    public void CalculateFees()
    {
        foreach (var vehicle in vehicles)
        {
            Console.WriteLine($"Parking fee: {vehicle.CalculateParkingFee(3)}");
        }
    }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در این مثال، هر زیر کلاس از Vehicle را می توان به ParkingLot، و مربوط به آنها CalculateParkingFee متدها به درستی فراخوانی خواهند شد.

اصل باز/بسته (OCP) برای توسعه پذیری

یک مدل دامنه غنی به ما اجازه می دهد تا عملکرد را بدون تغییر کد موجود گسترش دهیم. به عنوان مثال، اضافه کردن یک نوع وسیله نقلیه جدید مانند ElectricCar می توان با ایجاد یک زیر کلاس جدید از Vehicle، بدون تغییر منطق اصلی پارکینگ.

public class ElectricCar : Vehicle
{
    public override decimal CalculateParkingFee(int hours)
    {
        return hours * 3m; // Electric car parking fee logic
    }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این سیستم اکنون برای جا دادن گسترش یافته است ElectricCar بدون تغییر انواع خودروهای موجود یا منطق پارکینگ.

ترکیبی برای رفتارهای پیچیده

مدل‌های دامنه غنی اغلب از ترکیب برای ساختن موجودیت‌های پیچیده از موجودات ساده‌تر استفاده می‌کنند. به عنوان مثال، الف ParkingLot موجودیت ممکن است از چندین تشکیل شده باشد Level اشیایی که هر کدام شامل چندین است ParkingSpot اشیاء، امکان طراحی مدولار و انعطاف پذیر را فراهم می کند.

public class Level
{
    public int LevelNumber { get; set; }
    public ListParkingSpot> Spots { get; set; } = new ListParkingSpot>();
}

public class ParkingLot
{
    public ListLevel> Levels { get; set; } = new ListLevel>();
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این ترکیب به ما اجازه می دهد تا پارکینگ ها را با سطوح و نقاط مختلف به طور موثر مدیریت کنیم.

مدل دامنه کم خون: یک فرصت از دست رفته

در مقابل، یک مدل دامنه کم خون شامل موجودیت هایی است که کمی بیشتر از ساختارهای داده هستند و رفتار آنها در کلاس های خدمات جداگانه اجرا می شود. در حالی که این رویکرد می تواند کارساز باشد، بسیاری از مزایایی را که اصول OOP و SOLID ارائه می دهند از دست می دهد.
موجودیت ها اساسا دارندگان داده با گیرنده و تنظیم کننده هستند.
معایب: استفاده محدود از اصول OOP:
وراثت و چند شکلی: کمتر معنادار است زیرا منطق دامنه در جای دیگری قرار دارد.

محدود OOP ماe: در یک مدل کم خون با موجودیت های داده محور، فرصت کمتری برای وراثت و چندشکلی وجود دارد. تمرکز بر روی دستکاری داده ها است، نه رفتار پیچیده.
اصول جامد نقض نشده است (اما نه اهرمی): از آنجایی که موجودات کم خون دارای حداقل منطق هستند، نقض اصولی مانند جایگزینی Liskov دشوار است (رفتار زیادی برای جایگزینی وجود ندارد). با این حال، این اصول نیز در این زمینه فایده چندانی ندارند.

چند شکلی محدود

با جدا شدن رفتار از داده ها، فرصت کمتری برای استفاده از چند شکلی وجود دارد. به جای اینکه انواع خودروهای مختلف روش‌های محاسبه کارمزد خود را پیاده‌سازی کنند، ممکن است به یک کلاس خدمات واحد با یک بیانیه سوئیچ بزرگ برای رسیدگی به انواع مختلف برسیم.

public class ParkingFeeService
{
    public decimal CalculateFee(Vehicle vehicle, int hours)
    {
        switch (vehicle)
        {
            case Car _:
                return hours * 2.5m;
            case Motorcycle _:
                return hours * 1.5m;
            case Bus _:
                return hours * 5m;
            default:
                throw new ArgumentException("Unknown vehicle type");
        }
    }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این رویکرد انعطاف پذیری کمتری دارد و حفظ آن سخت تر است.

کپسولاسیون کاهش یافته

مدل‌های کم خون اغلب حالت داخلی خود را از طریق گیرنده‌ها و ستترها نشان می‌دهند و اصل کپسولاسیون را نقض می‌کنند. این می تواند به منطق تجاری پراکنده و افزایش جفت بین اجزا منجر شود.

public class Vehicle
{
    public string LicensePlate { get; set; }
    public int HoursParked { get; set; }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

سپس منطق کسب و کار به صورت خارجی مدیریت می شود و پیچیدگی را افزایش می دهد.

برنامه OCP طبیعی کمتر

بدون رفتار غنی در موجودیت‌ها، گسترش عملکرد اغلب مستلزم اصلاح کلاس‌های سرویس موجود است که اصل باز/بسته را نقض می‌کند.

ترکیب کم استفاده

مدل‌های کم‌خون به جای استفاده از قدرت ترکیب شی برای مدل‌سازی روابط و رفتارهای پیچیده دامنه، بیشتر به کد رویه‌ای در خدمات تکیه می‌کنند.

نتیجه گیری: پذیرش مدل دامنه غنی

در حالی که مدل‌های دامنه کم‌خون می‌توانند برای کاربردهای ساده CRUD کافی باشند، اما وقتی با منطق تجاری پیچیده سروکار دارند، کوتاهی می‌کنند. با پذیرش مدل‌های دامنه غنی، توسعه‌دهندگان می‌توانند پتانسیل کامل اصول OOP و SOLID را باز کنند:

  • موجودیت ها چیزی فراتر از حامل های داده می شوند. آنها رفتار را در بر می گیرند و واقعاً مفاهیم دامنه را نشان می دهند.
  • چند شکلی و وراثت را می توان برای ایجاد ساختارهای کد انعطاف پذیر و قابل استفاده مجدد مورد استفاده قرار داد.
  • اصول SOLID کاربردهای طبیعی پیدا می کند که منجر به سیستم های قابل نگهداری و توسعه پذیرتر می شود.
  • منطق دامنه پیچیده را می توان به وضوح و شهودی از طریق تعاملات شی بیان کرد.

یک مدل دامنه غنی نه تنها با فلسفه OOP هماهنگی بهتری دارد، بلکه پایه محکمی برای ساختن سیستم های نرم افزاری پیچیده و قابل نگهداری است که می تواند با تغییر نیازهای کسب و کار تکامل یابد.

به یاد داشته باشید، هدف OOP فقط گروه بندی داده ها و توابع با هم نیست، بلکه مدل سازی موثر دامنه مشکل است. با دادن رفتاری که به نهادهایتان شایسته است، فقط کد نمی نویسید. شما در حال ایجاد یک نمایش نرم افزاری از دامنه کسب و کار خود هستید که قدرتمند، انعطاف پذیر و واقعی است.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا