2008-10-18 29 views
5

Tôi có một lớp mà tôi muốn cho phép một số tùy chọn cấu hình (~ 20 +). Mỗi tùy chọn bật hoặc tắt một chức năng hoặc làm thay đổi hoạt động. Để tạo điều kiện này, tôi đã mã hóa một lớp tùy chọn riêng biệt với các giá trị mặc định. Tuy nhiên, tôi đã phải xả rác mã của tôi với điều kiện bảo vệ để xác định cách thức hoạt động của các phương thức. Tôi gần như đã xong, nhưng bây giờ mã có vẻ ngửi.Có một mẫu để thêm "tùy chọn" vào một lớp học không?

Có phương pháp/mẫu ưa thích nào để triển khai lớp như thế này không?

EDIT: Cụ thể hơn, tôi đang làm việc trên một lớp phân tích cú pháp. Mỗi tùy chọn cấu hình các phần loại trừ lẫn nhau của thuật toán phân tích cú pháp cơ bản. Ví dụ tôi có một số khu vực trong lớp học của tôi trông giống như dưới đây:

if (this.Option.UseIdAttribute) 
     attributeIDs = new Hashtable(); 
else 
     attributeIDs = null; 


    public Element GetElementById(string id) 
    { 
     if (string.IsNullOrEmpty (id)) 
      throw new ArgumentNullException("id"); 

     if (attributeIDs == null) 
      throw new Exception(ExceptionUseIdAttributeFalse); 

     return attributeIDs[id.ToLower()] as Element; 
    } 
+0

Nó rất khó để nói trừ khi bạn gửi mã ... – johnstok

Trả lời

5

Làm thế nào về Decorator pattern? Nó được thiết kế để tự động thêm hành vi vào một lớp.

+0

Tôi nghĩ về điều này, nhưng tôi sẽ xem xét mã một lần nữa để xem nếu nó sẽ phù hợp. Ban đầu tôi nghĩ rằng nó sẽ là một chút overkill, và tôi muốn tránh sưng lên lớp. – Nescio

+0

Mẫu trang trí cũng sẽ là câu trả lời của tôi. Bạn không cần phải sử dụng nó theo cùng một cách chính xác mà nó được trình bày trong cuốn sách GoF hoặc các mẫu thiết kế Headfirst - các mẫu không phải là công thức nấu ăn. Định hình nó để phù hợp với nhu cầu của bạn. – Sandman

0

Tôi đang nghĩ đến mẫu Command.

Nó sẽ giống như thế:

public class MyClass { 

    interface Command { 
    void execute(int a); 
    } 

    static class DoThisSimpleCommand implements Command { 
    void execute(int a) { 
     // do this simple 
    } 
    } 

    static class DoThisAnotherWayCommand implements Command { 
    void execute(int a) { 
     // do this another way 
    } 
    } 

    private Command doThisCommand; 

    MyClass() { 
    initDoThisCommand(); 
    } 

    private void initDoThisCommand() { 
    if (config.getDoThisMethod().equals("simple")) { 
     doThisCommand = new DoThisSimpleCommand(); 
    } else { 
     doThisCommand = new DoThisAnotherWayCommand(); 
    } 
    } 

    void doThis(int a) { 
    doThisCommand.execute(a); 
    } 
} 

Nói cách khác; bạn ủy thác trách nhiệm cho việc triển khai mà bạn nhanh chóng xây dựng. Bây giờ doThis của bạn chỉ là đại biểu và chức năng thực tế được dọn dẹp gọn gàng trong lớp riêng của nó.

1

Có một mẫu khác được gọi là mẫu trình xây dựng chính xác cho mục đích này. Thông thường, hữu ích khi bạn có một lớp nhưng mỗi tùy chọn 'cấu hình' có thể được thực hiện tùy chọn có lẽ chỉ trong một số kết hợp. (http://en.wikipedia.org/wiki/Builder_pattern, nhưng điều đó không mô tả chính xác kịch bản của tôi tho).

Bạn tạo hai lớp - lớp bạn muốn xây dựng và trình tạo. Lớp người xây dựng là lớp học chăm sóc làm việc kết hợp các tùy chọn là tùy chọn, kết hợp nào có ý nghĩa, v.v.

ví dụ: bạn muốn đại diện cho món salad - nhưng chỉ một số thành phần nhất định có vị tốt chỉ những người nên được thực hiện.

Class Salad { 
    private Veggie v; 
    private Egg e; 
    private Meat m; 
    // etc etc, lots of properties 
    //constructor like this is nice 
    Salad(SaladBuilder builder) { 
     //query the builder to actually build the salad object. 
     //getVeggie() will either return the supplied value, 
     //or a default if none exists. 
     this.v = builder.getVeggie(); 
     //rest of code omitted 
    } 

    //otherwise this constructor is fine, but needs a builder.build() method 
    Salad(Veggie v, Meat m, Egg e) { //code omitted 
    } 
} 

class SaladBuilder { 
    //some default, or left to null depending on what is needed 
    private Veggie v = SOME_DEFAULT_VEGGIE; 
    private Egg e; 
    private Meat m; 
    // etc etc, lots of properties. 

    //similar functions for each ingredient, 
    //or combination of ingredients that only make sense together 
    public SaladBuilder addIngredient(Meat m) { 
     this.m = m; 
     return this; 
    } 

    public SaladBuilder addIngredient(Veggie v) { 
     this.v = v; 
     return this; 
    } 

    public Salad build(){ 
     // essentially, creates the salad object, but make sure optionals 
     // are taken care of here. 
     return new Salad(getBeggie(), getMeat(), getEgg()); 
    } 
} 

sử dụng ví dụ

Salad s = new SaladBuilder().addIngredient(v).addIngredient(m).build(); 
2

Đối với các tùy chọn mà bật chức năng tắt/mở, tôi nghĩ Decorator là con đường để đi như @Thomas Owens nói. Tôi quan tâm hơn một chút về các tùy chọn thay đổi chức năng. Trang trí nhiều người không làm việc nếu các hoạt động này không thể bị xích. Ví dụ: nếu mã của bạn trông giống như:

public void ActionABC() 
{ 
    if (options.DoA) 
    { 
     A(); 
    } 

    if (options.DoB) 
    { 
     B(); 
    } 

    if (options.DoC) 
    { 
     C(); 
    } 
} 

public void ActionCAB() 
{ 
    if (options.DoC) 
    { 
     C(); 
    } 

    if (options.DoA) 
    { 
     A(); 
    } 

    if (options.DoB) 
    { 
     B(); 
    } 
} 

Điều này sẽ khó xử lý với Trang trí theo thứ tự bố cục khác nhau cho từng Phương thức hành động.

Với hơn 20 tùy chọn, giả sử rằng chúng đang bật/tắt, bạn có hơn 400 kết hợp có thể khác nhau. Tôi nghi ngờ rằng không phải tất cả các kết hợp này đều có khả năng như nhau. Đối với những thứ mà bạn không thể xử lý thông qua Decorator, bạn có thể muốn suy nghĩ về các chế độ hoạt động mà bản đồ trên để kết hợp các thiết lập. Chỉ hỗ trợ các chế độ được mong đợi nhất. Nếu số lượng chế độ nhỏ, bạn có thể xử lý việc này với phân lớp, sau đó sử dụng Decorator để thêm chức năng vào lớp con đại diện cho chế độ mà người dùng đã chọn.Bạn có thể sử dụng một Nhà máy để chọn và xây dựng lớp phù hợp dựa trên cấu hình.

Về bản chất, tôi đoán tôi muốn nói rằng bạn có thể muốn cân nhắc xem bạn có cần nhiều sự linh hoạt và phức tạp không, khi bạn đang xây dựng. Xem xét giảm số lượng tùy chọn cấu hình bằng cách thu gọn chúng thành một số lượng nhỏ hơn khả năng được sử dụng các chế độ.

+0

20+ Tùy chọn Boolean là hơn một triệu kết hợp, phải không? – erickson

0

Bạn có ý định thực thi các quy tắc cho các tùy chọn phụ thuộc lẫn nhau như thế nào? Nếu bạn có thể thêm các tùy chọn động, bạn có thể bị buộc phải có nhiều người được mời nếu bạn dùng mẫu lệnh, hoặc bạn có thể phải thực hiện một câu lệnh trường hợp để xây dựng các lệnh bạn cần thực thi.

1

Còn thứ gì đó như thế này thì sao?

IdMap elementsById = (options.useIdAttribute) ? new IdMapImpl() : new NullIdMap(); 

public Element getElementById(final string id) { 
    return elementsById.get(id); 
} 

Dựa trên các loại sau đây:

interface IdMap { 
    Element get(String id); 
} 

class NullIdMap implements IdMap { 
    public Element get(final String id) { 
     throw new Exception(/* Error message */); 
    } 
} 

class IdMapImpl implements IdMap { 
    Map<String, Element> elements = new HashMap<String, Element>(); 

    public Element get(final String id) { 
     rejectEmpty(id); 
     return elements.get(id.toLowerCase()); 
    } 
} 

Ở đây chúng ta tận dụng các mẫu NullObject để xử lý các trường hợp đặc biệt nơi useIdAttribute bị vô hiệu hóa. Tất nhiên có một sự cân bằng - lớp phân tích cú pháp chính nó là biểu cảm hơn, trong khi bây giờ có 4 kiểu thay vì 1. Phép tái cấu trúc này có vẻ quá mức cần thiết cho phương thức get, nhưng khi bạn thêm phương thức 'put()' vv nó có lợi ích của localisng logic 'trường hợp đặc biệt' trong một lớp đơn (NullIdMap).

[rejectEmpty là một phương pháp helper mà ném một ngoại lệ nếu được thông qua một chuỗi rỗng.]

+0

Ví dụ này là trong Java chứ không phải là C#, nhưng nó vẫn mang tính hướng dẫn. – johnstok

0

Sử dụng một cái gì đó giống như một chiến lược hoặc mẫu chính sách có thể có ích ở đây. Đó là một cách hay để đóng gói hoặc hoán đổi các triển khai khác nhau của thuật toán dựa trên những thứ như cấu hình hoặc (không) tồn tại của một số dữ liệu cụ thể trong thời gian chạy.

Trong trường hợp của bạn, nếu phân tích cú pháp của bạn thường có cùng loại hành vi (một cái gì đó trong, một cái gì đó) nhưng các thuật toán nội bộ thay đổi, đó là điều bạn có thể muốn thực hiện.

http://en.wikipedia.org/wiki/Strategy_pattern

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