2012-11-27 35 views
6

Vì tôi tương đối mới với OOP/C# tôi không biết đúng mẫu để giải quyết vấn đề này:Mẫu thiết kế hướng đối tượng để tránh nếu/sau đó/tuyên bố khác

Tôi phải xây dựng kiến ​​trúc plugin cho Nhà cung cấp IO khác nhau. Máy chủ đọc tên/kiểu nhà cung cấp cần thiết từ cấu hình, khi đó nhà cung cấp phải khởi tạo và tham số hóa.

Vì vậy, tôi có cơ bản giao diện này:

public interface IoProvider //(Base interface, all Providers implements this) 
{ 
    void Initialize(); 
    void Execute(); 
} 

public interface IFileProvider: IoProvider 
{ 
    string PropertyA { get; set; } 
} 

public interface ISmtpProvider : IoProvider 
{ 
    string PropertyB { get; set; } 
    string PropertyC { get; set; } 
} 

Như bạn thấy có nguồn gốc, các nhà cung cấp IO chuyên ngành có tính chất tham số bổ sung khác nhau mà giao diện cơ sở không có. Để tránh nếu/then/else hoặc chuyển báo cáo ý tưởng của tôi là sử dụng mẫu nhà máy. Nhưng nếu tôi hiểu điều này một cách chính xác thì nó sẽ không giải quyết được vấn đề nếu/sau đó của tôi vì trên máy khách, tôi phải kiểm tra kiểu dẫn xuất để cung cấp các thông số chính xác.

Vì vậy, dòng chảy chương trình trên máy chủ sẽ là một cái gì đó như thế này: chủ đọc cấu hình, được Tên/Loại cần cung cấp chủ gọi Nhà máy và được các nhà cung cấp

Nhưng làm thế nào tránh điều này - là có một mô hình để giải quyết điều này mà không cần nếu/thì/khác?

If (provider == typeOf(IFileProvider)) 
PropertyA = value 
else if (provider == typeOf(ISmtpProvider)) 
PropertyB = value 
PropertyC = value 
Elseif … 
+0

có gì sai với 'if/else'? –

+0

Tôi thích bản đồ cho loại điều này nhưng các nhà máy thường có các câu lệnh chuyển đổi lớn. Tại sao bạn cảm thấy cần phải loại bỏ nó? –

+2

tôi đã học được điều này là phong cách mã hóa xấu. không chỉ có 2 loại nhà cung cấp, tại thời điểm này có 5 nhà cung cấp khác nhau. – Radioactive

Trả lời

15

Bạn có thể thay thế tuyên bố switch bằng đa hình. Chỉ cho phép mỗi nhà cung cấp cấu hình chính nó từ cấu hình. Đây là một lựa chọn tốt nhất, bởi vì mỗi nhà cung cấp biết các giá trị để tìm kiếm:

provider.Configure(); 

Phương pháp này nên tồn tại trong giao diện cơ sở:

public interface IoProvider 
{ 
    void Initialize(); 
    void Execute(); 
    void Configure(); 
} 

Và mỗi nhà cung cấp thực hiện nó:

public interface ISmtpProvider : IoProvider 
{ 
    string PropertyB { get; set; } 
    string PropertyC { get; set; } 

    public void Configure() 
    { 
     PropertyB = ConfigurationManager.AppSettins["B"]; 
     PropertyB = ConfigurationManager.AppSettins["C"]; 
    } 
} 

Lợi ích chính của phương pháp này là bạn sẽ chỉ có một nơi để thay đổi, khi nhà cung cấp mới được thêm vào ứng dụng của bạn - chỉ cần thêm lớp nhà cung cấp mới, nơi biết cách cấu hình chính nó. Bạn không cần thay đổi cài đặt máy chủ. Và máy chủ của bạn sẽ đáp ứng nguyên tắc OCP - mở để mở rộng (bạn có thể thêm nhà cung cấp mới), nhưng đã đóng để sửa đổi (bạn không cần sửa đổi mã hiện tại khi nhà cung cấp mới thêm).

Ngoài ra, bạn có thể chuyển một số đối tượng cấu hình đến Configure(IConfiguration config) cho phương pháp này (nó sẽ làm cho mã của bạn có thể kiểm tra và không phụ thuộc vào tĩnh ConfigurationManager).

+0

vâng, điều đó có nghĩa là máy chủ phải có tất cả các tham số cho plugin. Nhưng trong thực tế, Host là một plugin (khác). Vì vậy, khi tôi thực hiện một IoProvider mới với các tham số mới tôi phải viết lại (mở rộng với các tham số mới) tất cả các plugin của Host. – Radioactive

+4

Bạn có thể xem xét việc truyền trong một số đối tượng cấu hình cho một phương thức như vậy, để tránh sự phụ thuộc vào các thiết lập chung. Làm cho thử nghiệm dễ dàng hơn, tôi nghĩ vậy. Nếu bạn vẫn muốn sử dụng các thiết lập chung, bạn thậm chí có thể làm như vậy thông qua một cái gì đó như 'provider.Configure (ConfigurationManager.AppSettings);'. – cHao

+0

@cHao đồng ý, việc chuyển đối tượng cấu hình là giải pháp tốt. Cũng đơn vị thử nghiệm sẽ được đơn giản trong trường hợp đó. –

0

Nếu bạn đã chết khi loại bỏ các câu lệnh if/else, bạn có thể có trình nạp trong lớp cơ sở, bạn chuyển tất cả các tham số vào, sau đó mỗi lớp quá tải và chỉ sử dụng các thông số họ cần.

Tôi sẽ cho rằng đó là một patter mong muốn chỉ nếu tất cả các lớp học có nguồn gốc của bạn được đảm bảo sử dụng cùng một bộ sưu tập các thông số nhỏ, theo những cách khác nhau. Nếu không, các câu hỏi if/else có thể là mong muốn

-2

Cuối cùng, bạn luôn phải sử dụng câu lệnh có điều kiện.

Chắc chắn, bạn có thể sử dụng một số thư viện để tóm tắt điều này. Chẳng hạn như một thư viện Dependency Injection và dựa vào một số hàm tạo, bạn có thể thực hiện điều này mà không cần ifs.

Nhưng ...

của nó thực sự không có giá trị nó, bạn sẽ thêm rất nhiều phức tạp để mã của bạn chỉ để làm cho nó thêm thanh lịch và sự đánh đổi nó thường không có giá trị nó.

Thats không nói rằng bạn không thể làm điều gì đó tốt hơn. Chụp từ hông, tôi sẽ thêm một phương thức như thế này vào giao diện.

void Init (thông số từ điển) và trong mỗi lần triển khai bạn đọc từ điển và khởi tạo các thuộc tính cần thiết

+0

Normaly tôi có thể sống với các câu lệnh có điều kiện nhưng trong trường hợp của tôi tôi phải viết lại các máy chủ. (Host chính nó là một plugin - đọc ở trên trong ý kiến ​​của tôi) – Radioactive

Các vấn đề liên quan