RẮN, tránh miền thiếu máu, tiêm phụ thuộc?


33

Mặc dù đây có thể là một câu hỏi bất khả tri về ngôn ngữ lập trình, tôi quan tâm đến câu trả lời nhắm vào hệ sinh thái .NET.

Đây là kịch bản: giả sử chúng ta cần phát triển một ứng dụng bảng điều khiển đơn giản cho quản trị công cộng. Ứng dụng này là về thuế xe. Họ (chỉ) có các quy tắc kinh doanh sau:

1.a) Nếu chiếc xe là một chiếc xe hơi và lần cuối cùng chủ sở hữu của nó đã nộp thuế là 30 ngày trước thì chủ sở hữu phải trả lại.

1.b) Nếu phương tiện là xe máy và lần cuối cùng chủ sở hữu đã nộp thuế là 60 ngày trước thì chủ sở hữu phải trả lại.

Nói cách khác, nếu bạn có một chiếc ô tô bạn phải trả tiền cứ sau 30 ngày hoặc nếu bạn có một chiếc xe máy, bạn phải trả tiền sau mỗi 60 ngày.

Đối với mỗi phương tiện trong hệ thống, ứng dụng nên kiểm tra các quy tắc đó và in các phương tiện đó (số biển và thông tin chủ sở hữu) không thỏa mãn chúng.

Điều tôi muốn là:

2.a) Tuân thủ các nguyên tắc RẮN (đặc biệt là nguyên tắc Mở / Đóng).

Điều tôi không muốn là (tôi nghĩ):

2.b) Một miền thiếu máu, do đó logic kinh doanh nên đi vào bên trong các thực thể kinh doanh.

Tôi bắt đầu với điều này:

public class Person
// You wanted a banana but what you got was a gorilla holding the banana and the entire jungle.
{
    public string Name { get; set; }

    public string Surname { get; set; }
}

public abstract class Vehicle
{
    public string PlateNumber { get; set; }

    public Person Owner { get; set; }

    public DateTime LastPaidTime { get; set; }

    public abstract bool HasToPay();
}

public class Car : Vehicle
{
    public override bool HasToPay()
    {
        return (DateTime.Today - this.LastPaidTime).TotalDays >= 30;
    }
}

public class Motorbike : Vehicle
{
    public override bool HasToPay()
    {
        return (DateTime.Today - this.LastPaidTime).TotalDays >= 60;
    }
}

public class PublicAdministration
{
    public IEnumerable<Vehicle> GetVehiclesThatHaveToPay()
    {
        return this.GetAllVehicles().Where(vehicle => vehicle.HasToPay());
    }

    private IEnumerable<Vehicle> GetAllVehicles()
    {
        throw new NotImplementedException();
    }
}

class Program
{
    static void Main(string[] args)
    {
        PublicAdministration administration = new PublicAdministration();
        foreach (var vehicle in administration.GetVehiclesThatHaveToPay())
        {
            Console.WriteLine("Plate number: {0}\tOwner: {1}, {2}", vehicle.PlateNumber, vehicle.Owner.Surname, vehicle.Owner.Name);
        }
    }
}

2.a: Nguyên tắc mở / đóng được đảm bảo; nếu họ muốn thuế xe đạp bạn chỉ cần thừa kế từ Xe, thì bạn ghi đè phương thức HasToPay và bạn đã hoàn thành. Nguyên tắc mở / đóng thỏa mãn thông qua thừa kế đơn giản.

Các "vấn đề" là:

3.a) Tại sao một chiếc xe phải biết nếu nó phải trả tiền? Không phải là một mối quan tâm công khai? Nếu quy tắc hành chính công thay đổi thuế xe hơi, tại sao Xe phải thay đổi? Tôi nghĩ bạn nên hỏi: "vậy thì tại sao bạn lại đặt phương thức HasToPay bên trong Xe?", Và câu trả lời là: bởi vì tôi không muốn kiểm tra loại phương tiện (typeof) trong PublicAd dùng. Bạn có thấy một sự thay thế tốt hơn?

3 Một giải pháp có thể là: chuyển phương thức HasToPay sang một lớp khác, chúng ta có thể gọi nó là TaxPayment. Sau đó, chúng tôi tạo ra hai lớp dẫn xuất TaxPayment; CarTaxPayment và MotorbikeTaxPayment. Sau đó, chúng tôi thêm một thuộc tính Thanh toán trừu tượng (thuộc loại TaxPayment) vào lớp Xe và chúng tôi trả lại ví dụ TaxPayment đúng từ các lớp Xe hơi và Xe máy:

public abstract class TaxPayment
{
    public abstract bool HasToPay();
}

public class CarTaxPayment : TaxPayment
{
    public override bool HasToPay()
    {
        return (DateTime.Today - this.LastPaidTime).TotalDays >= 30;
    }
}

public class MotorbikeTaxPayment : TaxPayment
{
    public override bool HasToPay()
    {
        return (DateTime.Today - this.LastPaidTime).TotalDays >= 60;
    }
}

public abstract class Vehicle
{
    public string PlateNumber { get; set; }

    public Person Owner { get; set; }

    public DateTime LastPaidTime { get; set; }

    public abstract TaxPayment Payment { get; }
}

public class Car : Vehicle
{
    private CarTaxPayment payment = new CarTaxPayment();
    public override TaxPayment Payment
    {
        get { return this.payment; }
    }
}

public class Motorbike : Vehicle
{
    private MotorbikeTaxPayment payment = new MotorbikeTaxPayment();
    public override TaxPayment Payment
    {
        get { return this.payment; }
    }
}

Và chúng tôi gọi quy trình cũ theo cách này:

    public IEnumerable<Vehicle> GetVehiclesThatHaveToPay()
    {
        return this.GetAllVehicles().Where(vehicle => vehicle.Payment.HasToPay());
    }

Nhưng bây giờ mã này sẽ không được biên dịch vì không có thành viên LastPaidTime trong CarTaxPayment / MotorbikeTaxPayment / TaxPayment. Tôi bắt đầu thấy CarTaxPayment và MotorbikeTaxPayment giống như "triển khai thuật toán" hơn là các thực thể kinh doanh. Bằng cách nào đó bây giờ những "thuật toán" đó cần giá trị LastPaidTime. Chắc chắn, chúng ta có thể chuyển giá trị cho người xây dựng TaxPayment, nhưng điều đó sẽ vi phạm đóng gói phương tiện, hoặc trách nhiệm, hoặc bất cứ điều gì mà những người truyền giáo OOP gọi nó, phải không?

3.c) Giả sử chúng ta đã có ObjectContext của Entity Framework (sử dụng các đối tượng miền của chúng ta; Person, Xe, Xe hơi, Xe máy, Công khai). Bạn sẽ làm thế nào để có được một tham chiếu ObjectContext từ phương thức PublicAd dùng.GetAllVehicles và do đó thực hiện chức năng này?

3.d) Nếu chúng ta cũng cần cùng một tham chiếu ObjectContext cho CarTaxPayment.HasToPay nhưng một tài liệu khác cho MotorbikeTaxPayment.HasToPay, người chịu trách nhiệm "tiêm" hoặc bạn sẽ chuyển các tham chiếu đó đến các lớp thanh toán như thế nào? Trong trường hợp này, tránh một miền thiếu máu (và do đó không có đối tượng dịch vụ), không dẫn chúng ta đến thảm họa?

Lựa chọn thiết kế của bạn cho kịch bản đơn giản này là gì? Rõ ràng các ví dụ tôi đã trình bày là "quá phức tạp" cho một nhiệm vụ dễ dàng, nhưng đồng thời, ý tưởng là tuân thủ các nguyên tắc RẮN và ngăn chặn các miền thiếu máu (trong đó đã được ủy nhiệm để phá hủy).

Câu trả lời:


15

Tôi sẽ nói rằng cả người và phương tiện đều không nên biết liệu có phải trả tiền hay không.

xem xét lại miền vấn đề

Nếu bạn nhìn vào miền vấn đề "người có xe hơi": Để ​​mua xe, bạn có một loại hợp đồng chuyển quyền sở hữu từ người này sang người khác, cả xe lẫn người đều không thay đổi trong quy trình đó. Mô hình của bạn là hoàn toàn thiếu đó.

thử nghiệm suy nghĩ

Giả sử có một cặp vợ chồng, người đàn ông sở hữu chiếc xe và anh ta đang trả thuế. Bây giờ người đàn ông chết, vợ anh ta thừa kế chiếc xe và sẽ đóng thuế. Tuy nhiên, các tấm của bạn sẽ giữ nguyên (ít nhất là ở nước tôi họ sẽ). Nó vẫn là chiếc xe giống nhau! Bạn sẽ có thể mô hình hóa trong miền của bạn.

=> Quyền sở hữu

Một chiếc xe không thuộc sở hữu của bất cứ ai vẫn là một chiếc xe nhưng không ai sẽ trả thuế cho nó. Trên thực tế, bạn không phải trả thuế cho chiếc xe mà là đặc quyền sở hữu một chiếc xe hơi . Vì vậy, đề xuất của tôi sẽ là:

public class Person
{
    public string Name { get; set; }

    public string Surname { get; set; }

    public List<Ownership> getOwnerships();

    public List<Vehicle> getVehiclesOwned();
}

public abstract class Vehicle
{
    public string PlateNumber { get; set; }

    public Ownership currentOwnership { get; set; }

}

public class Car : Vehicle {}

public class Motorbike : Vehicle {}

public abstract class Ownership
{
    public Ownership(Vehicle vehicle, Owner owner);

    public Vehicle Vehicle { get;}

    public Owner CurrentOwner { get; set; }

    public abstract bool HasToPay();

    public DateTime LastPaidTime { get; set; }

   //Could model things like payment history or owner history here as well
}

public class CarOwnership : Ownership
{
    public override bool HasToPay()
    {
        return (DateTime.Today - this.LastPaidTime).TotalDays >= 30;
    }
}

public class MotorbikeOwnership : Ownership
{
    public override bool HasToPay()
    {
        return (DateTime.Today - this.LastPaidTime).TotalDays >= 60;
    }
}

public class PublicAdministration
{
    public IEnumerable<Ownership> GetVehiclesThatHaveToPay()
    {
        return this.GetAllOwnerships().Where(HasToPay());
    }

}

Điều này là không hoàn hảo và có lẽ thậm chí không chính xác mã C # từ xa nhưng tôi hy vọng bạn có ý tưởng (và ai đó có thể làm sạch điều này). Nhược điểm duy nhất là bạn có thể sẽ cần một nhà máy hoặc một cái gì đó để tạo ra sự chính xác CarOwnershipgiữa a Carvà aPerson


Tôi nghĩ rằng chính chiếc xe nên ghi lại các khoản thuế phải trả cho nó, và các cá nhân nên ghi lại các khoản thuế phải trả cho tất cả các phương tiện của họ. Mã số thuế có thể được viết để nếu thuế trên xe hơi được trả vào ngày 1 tháng 6 và được bán vào ngày 10 tháng 6, chủ sở hữu mới có thể phải chịu thuế vào ngày 10 tháng 6, ngày 1 tháng 7 hoặc ngày 10 tháng 7 (và ngay cả khi đó hiện tại một cách nó có thể thay đổi). Nếu quy tắc 30 ngày là không đổi đối với chiếc xe ngay cả khi thay đổi quyền sở hữu, nhưng những chiếc xe bị nợ không ai không thể trả thuế, thì tôi sẽ đề nghị rằng khi một chiếc xe không có tên trong 30 ngày trở lên được mua, một nộp thuế ...
supercat

... được ghi nhận từ một người "tín dụng thuế xe hơi" hư cấu, do đó, kể từ thời điểm bán, nghĩa vụ thuế sẽ là 0 hoặc 30 ngày, bất kể chiếc xe đã được đặt tên bao lâu.
supercat

4

Tôi nghĩ rằng trong tình huống này, nơi bạn muốn các quy tắc kinh doanh tồn tại bên ngoài các đối tượng mục tiêu, việc kiểm tra loại là hơn mức chấp nhận được.

Chắc chắn, trong nhiều tình huống, bạn nên sử dụng một người cha Chiến lược và để các đối tượng tự xử lý mọi việc, nhưng điều đó không phải lúc nào cũng đáng mong muốn.

Trong thế giới thực, một nhân viên bán hàng sẽ xem xét loại phương tiện và tra cứu dữ liệu thuế dựa trên đó. Tại sao phần mềm của bạn không tuân theo quy tắc busienss giống nhau?


Ok, giả sử bạn đã thuyết phục tôi và từ phương pháp GetVehiclesThatHaveToPay, chúng tôi kiểm tra loại xe. Ai nên chịu trách nhiệm cho LastPaidTime? LastPaidTime là mối quan tâm của Xe cộ hay Công khai? Bởi vì nếu đó là Xe, logic kinh doanh đó không nằm trong Xe? Bạn có thể cho tôi biết gì về câu hỏi 3.c?

@DavidRobertJones - Lý tưởng nhất là tôi sẽ tra cứu khoản thanh toán cuối cùng trong nhật ký lịch sử Thanh toán, dựa trên giấy phép xe. Một khoản thanh toán thuế là một mối quan tâm về thuế, không phải là một mối quan tâm phương tiện.
Erik Funkenbusch

5
@DavidRobertJones Tôi nghĩ bạn đang nhầm lẫn bản thân với phép ẩn dụ của riêng bạn. Những gì bạn có không phải là một chiếc xe thực sự cần phải làm tất cả những điều một chiếc xe làm. Thay vào đó, bạn đã đặt tên, để đơn giản là TaxableVehicle (hoặc một cái gì đó tương tự) một Phương tiện. Toàn bộ tên miền của bạn là thanh toán thuế. Đừng lo lắng về những gì xe thực tế làm. Và, nếu cần thiết hãy bỏ ẩn dụ và đưa ra một cách thích hợp hơn.

3

Điều tôi không muốn là (tôi nghĩ):

2.b) Một miền thiếu máu, có nghĩa là logic kinh doanh bên trong các thực thể kinh doanh.

Bạn có điều này ngược. Một miền thiếu máu là một miền mà logic nằm ngoài các thực thể kinh doanh. Có rất nhiều lý do tại sao sử dụng các lớp bổ sung để thực hiện logic là một ý tưởng tồi; và chỉ có một vài lý do tại sao bạn nên làm điều đó.

Do đó, lý do tại sao nó được gọi là thiếu máu: lớp học không có gì trong đó ..

Tôi sẽ làm gì:

  1. Thay đổi lớp Xe trừu tượng của bạn thành một giao diện.
  2. Thêm giao diện ITaxPayment mà các phương tiện phải thực hiện. Có HasToPay của bạn đi chơi ở đây.
  3. Xóa các lớp MotorbikeTaxPayment, CarTaxPayment, TaxPayment. Chúng là những biến chứng không cần thiết / lộn xộn.
  4. Mỗi loại phương tiện (ô tô, xe đạp, motorhome) nên thực hiện ở mức tối thiểu Xe và nếu chúng bị đánh thuế, cũng nên thực hiện ITaxPayment. Bằng cách này, bạn có thể phân định giữa những phương tiện không bị đánh thuế và những phương tiện ... Giả sử tình huống này thậm chí còn tồn tại.
  5. Mỗi loại phương tiện nên thực hiện, trong phạm vi ranh giới của chính lớp đó, các phương thức được xác định trong giao diện của bạn.
  6. Ngoài ra, thêm ngày thanh toán cuối cùng vào giao diện ITaxPayment của bạn.

Bạn đúng. Ban đầu câu hỏi không được đặt ra theo cách đó, tôi đã xóa một phần và ý nghĩa đã thay đổi. Bây giờ nó đã được sửa. Cảm ơn!

2

Tại sao một chiếc xe phải biết nếu nó phải trả tiền? Không phải là một mối quan tâm công khai?

Không. Theo tôi hiểu thì toàn bộ ứng dụng của bạn là về thuế. Vì vậy, mối quan tâm về việc liệu một chiếc xe có nên bị đánh thuế không còn là mối quan tâm của PublicAdAd sau đó là bất cứ điều gì khác trong toàn bộ chương trình. Không có lý do để đặt nó bất cứ nơi nào khác ngoài đây.

public class Car : Vehicle
{
    public override bool HasToPay()
    {
        return (DateTime.Today - this.LastPaidTime).TotalDays >= 30;
    }
}

public class Motorbike : Vehicle
{
    public override bool HasToPay()
    {
        return (DateTime.Today - this.LastPaidTime).TotalDays >= 60;
    }
}

Hai đoạn mã này chỉ khác nhau bởi hằng số. Thay vì ghi đè toàn bộ hàm HasToPay () ghi đè một int DaysBetweenPayments()hàm. Gọi đó thay vì sử dụng hằng. Nếu đây là tất cả những gì họ thực sự khác nhau, tôi sẽ bỏ các lớp học.

Tôi cũng đề nghị Xe máy / Xe hơi phải là một loại tài sản của phương tiện chứ không phải là các lớp con. Điều đó cho phép bạn linh hoạt hơn. Ví dụ, nó giúp bạn dễ dàng thay đổi danh mục xe máy hoặc ô tô. Tất nhiên xe đạp không có khả năng biến thành xe hơi ngoài đời thực. Nhưng bạn biết rằng một chiếc xe sẽ bị nhập sai và cần phải được thay đổi sau đó.

Chắc chắn, chúng ta có thể chuyển giá trị cho người xây dựng TaxPayment, nhưng điều đó sẽ vi phạm đóng gói phương tiện, hoặc trách nhiệm, hoặc bất cứ điều gì mà những người truyền giáo OOP gọi nó, phải không?

Không hẳn vậy. Một đối tượng cố tình chuyển dữ liệu của mình sang đối tượng khác làm tham số không phải là mối quan tâm đóng gói lớn. Mối quan tâm thực sự là các đối tượng khác tiếp cận bên trong đối tượng này để kéo dữ liệu ra hoặc thao tác dữ liệu bên trong đối tượng.


1

Bạn có thể đang đi đúng hướng. Vấn đề là, như bạn đã nhận thấy, không có thành viên LastPaidTime trong TaxPayment . Đó chính xác là nơi nó thuộc về, mặc dù nó không cần phải được phơi bày công khai:

public interface ITaxPayment
{
    // Your base TaxPayment class will know the 
    // next due date. But you can easily create
    // one which uses (for example) fixed dates
    bool IsPaymentDue();
}

public interface IVehicle
{
    string Plate { get; }
    Person Owner { get; }
    ITaxPayment LastTaxPayment { get; }
} 

Mỗi lần bạn nhận được một khoản thanh toán, bạn tạo một phiên bản mới ITaxPaymenttheo chính sách hiện tại, có thể có các quy tắc hoàn toàn khác với quy tắc trước đó.


Vấn đề là khoản thanh toán đến hạn phụ thuộc vào lần cuối cùng chủ sở hữu thanh toán (các phương tiện khác nhau, ngày đáo hạn khác nhau). Làm thế nào ITaxPayment biết đó là lần cuối cùng một chiếc xe (hoặc chủ sở hữu) trả tiền để thực hiện tính toán?

1

Tôi đồng ý với câu trả lời của @sebastiangeiger rằng vấn đề thực sự là về quyền sở hữu phương tiện chứ không phải phương tiện. Tôi sẽ đề nghị sử dụng câu trả lời của anh ấy nhưng với một vài thay đổi. Tôi sẽ làm cho Ownershiplớp một lớp chung:

public class Vehicle {}

public abstract class Ownership<T>
{
    public T Item { get; set; }

    public DateTime LastPaidTime { get; set; }

    public abstract TimeSpan PaymentInterval { get; }

    public virtual bool HasToPay
    {
        get { return (DateTime.Today - this.LastPaidTime) >= this.PaymentInterval; }
    }
}

Và sử dụng tính kế thừa để chỉ định khoảng thời gian thanh toán cho từng loại xe. Tôi cũng đề nghị sử dụng TimeSpanlớp được gõ mạnh thay vì số nguyên đơn giản:

public class CarOwnership : Ownership<Vehicle>
{
    public override TimeSpan PaymentInterval
    {
        get { return new TimeSpan(30, 0, 0, 0); }
    }
}

public class MotorbikeOwnership : Ownership<Vehicle>
{
    public override TimeSpan PaymentInterval
    {
        get { return new TimeSpan(60, 0, 0, 0); }
    }
}

Bây giờ mã thực tế của bạn chỉ cần xử lý một lớp - Ownership<Vehicle>.

public class PublicAdministration
{
    List<Ownership<Vehicle>> VehicleOwnerships { get; set; }

    public IEnumerable<Vehicle> GetVehiclesThatHaveToPay()
    {
        return VehicleOwnerships.Where(x => x.HasToPay).Select(x => x.Item);
    }
}

0

Tôi ghét phải chia nó cho bạn, nhưng bạn có một miền thiếu máu , tức là logic kinh doanh bên ngoài các đối tượng. Nếu đây là những gì bạn sẽ làm cho nó là ổn, nhưng bạn nên đọc về lý do để tránh một.

Cố gắng không mô hình hóa miền vấn đề, nhưng mô hình hóa giải pháp. Đó là lý do tại sao bạn kết thúc với hệ thống phân cấp thừa kế song song và phức tạp hơn bạn cần.

Một cái gì đó như thế này là tất cả những gì bạn cần cho ví dụ đó:

public class Vehicle
{
    public Boolean isMotorBike()
    public string PlateNumber { get; set; }
    public String Owner { get; set; }
    public DateTime LastPaidTime { get; set; }
}

public class TaxPaymentCalculator
{
    public List<Vehicle> GetVehiclesThatHaveToPay(List<Vehicle> all)
}

Nếu bạn muốn theo RẮN thì thật tuyệt, nhưng như chú Bob tự nói, chúng chỉ là những nguyên tắc kỹ thuật . Chúng không có nghĩa là được áp dụng nghiêm ngặt, nhưng là những hướng dẫn cần được xem xét.


4
Tôi nghĩ rằng giải pháp của bạn không đặc biệt có thể mở rộng. Bạn sẽ cần thêm một boolean mới cho từng loại xe bạn thêm. Đó là một lựa chọn kém trong quan điểm của tôi.
Erik Funkenbusch

@Garrett Hall, tôi thích bạn xóa lớp Person khỏi "miền" của tôi. Tôi sẽ không có lớp học đó ngay từ đầu, rất xin lỗi về điều đó. Nhưng isMotorBike không có ý nghĩa. Đó sẽ là việc thực hiện?

1
@DavidRobertJones: Tôi đồng ý với Mystere, thêm một isMotorBikephương thức không phải là một lựa chọn thiết kế OOP tốt. Nó giống như thay thế đa hình bằng mã loại (mặc dù là boolean).
Groo

@MystereMan, người ta có thể dễ dàng thả các bool và sử dụng mộtenum Vehicles
radarbob

+1. Cố gắng không mô hình hóa miền vấn đề, nhưng mô hình hóa giải pháp . Pithy . Đáng nhớ nói. VÀ các nguyên tắc bình luận. Tôi thấy quá nhiều nỗ lực để giải thích nghĩa đen nghiêm ngặt ; vô lý.
radarbob

0

Tôi nghĩ rằng bạn đúng rằng thời hạn thanh toán không phải là tài sản của Ô tô / Xe máy thực tế. Nhưng nó là một thuộc tính của lớp xe.

Mặc dù bạn có thể đi theo con đường có if / select trên Loại đối tượng, nhưng điều này không có khả năng mở rộng. Nếu sau này bạn thêm xe tải và Segway và đẩy xe đạp và xe buýt và máy bay (có vẻ như không thể, nhưng bạn không bao giờ biết với các dịch vụ công cộng) thì điều kiện sẽ trở nên lớn hơn. Và nếu bạn muốn thay đổi các quy tắc cho một chiếc xe tải, bạn phải tìm nó trong danh sách đó.

Vậy tại sao không biến nó, theo đúng nghĩa đen, một thuộc tính và sử dụng sự phản chiếu để kéo nó ra? Một cái gì đó như thế này:

[DaysBetweenPayments(30)]
public class Car : Vehicle
{
    // actual properties of the physical vehicle
}

[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class DaysBetweenPaymentsAttribute : Attribute, IPaymentRequiredCalculator
{
    private int _days;

    public DaysBetweenPaymentAttribute(int days)
    {
        _days = days;
    }

    public bool HasToPay(Vehicle vehicle)
    {
        return vehicle.LastPaid.AddDays(_days) <= DateTime.Now;
    }
}

Bạn cũng có thể đi với một biến tĩnh, nếu điều đó thoải mái hơn.

Nhược điểm là

  • rằng nó sẽ chậm hơn một chút và tôi cho rằng bạn phải xử lý rất nhiều phương tiện. Nếu đó là một vấn đề thì tôi có thể có một cái nhìn thực tế hơn và gắn bó với thuộc tính trừu tượng hoặc cách tiếp cận giao thoa. (Mặc dù, tôi cũng sẽ xem xét bộ nhớ đệm theo loại.)

  • Bạn sẽ phải xác thực khi khởi động rằng mỗi lớp con Xe có thuộc tính IPaymentRequiredCalculator (hoặc cho phép mặc định), vì bạn không muốn nó xử lý 1000 ô tô, chỉ bị sập vì Motorbike không có PaymentPeriod.

Ưu điểm là nếu sau này bạn gặp phải một tháng theo lịch hoặc thanh toán hàng năm, bạn chỉ có thể viết một lớp thuộc tính mới. Đây là định nghĩa của OCP.


Vấn đề chính với điều này là nếu bạn muốn thay đổi số ngày giữa các lần thanh toán cho Xe, bạn sẽ cần phải xây dựng lại và triển khai lại, và nếu tần suất thanh toán thay đổi dựa trên một thuộc tính của thực thể (ví dụ: kích thước động cơ) thì sẽ khó có được một sửa chữa thanh lịch cho điều đó.
Ed James

@EdWoodcock: Tôi nghĩ rằng vấn đề đầu tiên là đúng với hầu hết các giải pháp và nó thực sự sẽ không làm tôi lo lắng. Nếu họ muốn mức độ linh hoạt đó sau này, tôi sẽ cung cấp cho họ một ứng dụng DB. Về điểm thứ hai, tôi hoàn toàn không đồng ý. Tại thời điểm đó, bạn chỉ cần thêm phương tiện làm đối số tùy chọn vào HasToPay () và bỏ qua nó ở nơi bạn không cần.
pdr

Tôi đoán nó phụ thuộc vào các kế hoạch dài hạn mà bạn có cho ứng dụng, cộng với các yêu cầu cụ thể của khách hàng, về việc nó có đáng để cắm DB hay không nếu tôi chưa tồn tại (tôi có xu hướng cho rằng khá nhiều phần mềm là DB -driven, mà tôi biết không phải lúc nào cũng như vậy). Tuy nhiên, về điểm thứ hai, vòng loại quan trọng là thanh lịch ; Tôi sẽ không nói rằng việc thêm một tham số phụ vào một phương thức trên một thuộc tính mà sau đó lấy một thể hiện của lớp mà thuộc tính được đính kèm là một giải pháp đặc biệt tao nhã. Nhưng, một lần nữa, nó phụ thuộc vào yêu cầu khách hàng của bạn.
Ed James

@EdWoodcock: Thực ra, tôi chỉ nghĩ về điều này và đối số lastPaid là đã sẽ phải là một tài sản của xe. Với ý kiến ​​của bạn trong đầu, có thể đáng để thay thế đối số đó ngay bây giờ, thay vì sau này - sẽ chỉnh sửa.
pdr
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.