2012-04-04 30 views
7

Disclaimer: Tôi rất thích được sử dụng dependency injection về dự án này và có một thiết kế giao diện dựa trên lỏng lẻo trên diện rộng, nhưng việc sử dụng phụ thuộc-tiêm đã bị bắn rơi trong dự án này. Ngoài ra SOLID nguyên tắc thiết kế (và design patterns nói chung) là một cái gì đó nước ngoài, nơi tôi làm việc và tôi mới với nhiều người trong số họ bản thân mình. Vì vậy, hãy xem xét việc xem xét khi đề xuất thiết kế tốt hơn cho vấn đề này.Có tùy chọn thiết kế tốt hơn không?

Đây là phiên bản đơn giản của mã tôi đang làm việc và có vẻ như nó có vẻ bị giả mạo. Nếu vậy tôi xin lỗi. Hãy xem xét các loại sau đây:

// Foo is a class that wraps underlying functionality from another 
// assembly to create a simplified API. Think of this as a service layer class, 
// a facade-like wrapper. It contains a helper class that is specific to 
// foo. Other AbstractFoo implementations have their own helpers. 

public class Foo : AbstractFoo 
{ 
    private readonly DefaultHelper helper; 
    public override DefaultHelper Helper { get { return helper; } } 

    public Foo() 
    { 
     helper = new Helper("custom stuff"); 
    } 

    public override void Operation1(string value) 
    { 
     Console.WriteLine("Operation1 using " + value); 
    } 

    public override void Operation2() 
    { 
     Console.WriteLine("Operation2"); 
    } 
} 

// Helper derives from a default implementation and allows us to 
// override it's methods to do things specific for the class that 
// holds this helper. Sometimes we use a custom helper, sometimes 
// we use the default one. 

public class Helper : DefaultHelper 
{ 
    private readonly string customStuff; 

    public Helper(string value) 
    { 
     customStuff = value; 
    } 

    public override void DoSomethingHelpful() 
    { 
     Console.WriteLine("I was helpful using " + customStuff); 
    } 
} 

Giả sử hai lớp được sử dụng như sau:

// foo referenced and used in one part of code 
    var foo = new Foo(); 
    foo.Operation2(); // or foo.Operation1(); 

    // some other point in the program where we don't have a reference to foo 
    // but do have a reference to the helper 
    helper.DoSomethingHelpful(); 

Tuy nhiên bây giờ tôi nhận ra rằng tôi cũng cần phải thực hiện foo.Operation1 trong một số triển khai của helper.DoSomethingHelpful();? Các cách giải quyết tiềm năng mà tôi nghĩ đến sẽ là:

  1. Có foo và người trợ giúp có mối quan hệ hai chiều. Vì vậy mà trong DoSomethingHelpful chúng ta có thể gọi foo.Operation2
  2. đã foo thực hiện giao diện IHelp và di chuyển "helper" mã vào foo
  3. Sử dụng đoàn và thông qua phương pháp Operation2 như một đại biểu Action<string> vào constructor của Helper.

Không ai trong số những phương pháp dường như là lý tưởng (mặc dù tôi đã khá xác định nhiều tôi không thích tùy chọn 1 và đang lo lắng về khả năng bảo trì với tùy chọn 3 nếu chúng ta tìm hiểu sau đó chúng tôi cần phải vượt qua trong nhiều đại biểu). Điều này khiến tôi tự hỏi liệu có vấn đề với thiết kế ban đầu của combo Helper/Foo hay không. Suy nghĩ?

+0

Tôi nghĩ bạn có nghĩa là 'DefaultHelper' thay vì' DefualtHelper'. – ja72

+0

@ ja72, chính tả không bao giờ là bộ đồ mạnh mẽ của tôi, tôi đã cập nhật lỗi đánh máy này. – Matt

+1

+1 cho tuyên bố từ chối trách nhiệm ... Đã có một tiếng cười lớn. – scottheckel

Trả lời

1

"Không ai trong số những phương pháp dường như là lý tưởng (mặc dù tôi đã khá nhiều xác định tôi không thích phương án 1 và đang lo lắng về khả năng bảo trì với tùy chọn 3 nếu chúng ta tìm hiểu sau đó chúng tôi cần phải vượt qua trong Điều này khiến tôi tự hỏi liệu có vấn đề gì với thiết kế ban đầu của combo Helper/Foo. "

Bạn nói đúng - có vấn đề với thiết kế của Trình trợ giúp và Foo. Mối quan hệ Foo/Helper cơ bản như ban đầu bạn mô tả nó là tốt, và là một mô hình phổ biến khi bạn phải bọc các đối tượng khác mà bạn không kiểm soát. Nhưng sau đó bạn nói:

"Nếu tôi tìm ra rằng tôi cũng cần phải thực hiện foo.Operation1 trong một số triển khai của helper.DoSomethingHelpful() ;?"

Đây là nơi chúng tôi gặp sự cố. Bạn bắt đầu mô tả mối quan hệ mà Foo phụ thuộc vào Người trợ giúp; bây giờ bạn đang mô tả một mối quan hệ mà Helper phụ thuộc vào Foo. Điều đó ngay lập tức cho tôi biết rằng các mối quan hệ phụ thuộc của bạn bị rối tung lên. Các mối quan hệ phụ thuộc giữa các đối tượng chỉ nên đi một chiều; trong thực tế phụ thuộc tiêm dựa vào điều này.

+0

+1 Tôi đồng ý ở đây và cảm ơn bạn đã xác định lý do tôi không thích tùy chọn 1 và 3 (tức là phụ thuộc bị rối). Và tại sao tôi nghĩ rằng kết hợp các chức năng của Foo và Helper trong trường hợp này có ý nghĩa do sự phụ thuộc lẫn nhau của chúng. Sử dụng giao diện IHelp tôi vẫn có thể đối xử với Foo như thể nó là một người trợ giúp ở nơi khác trong mã. – Matt

1

Tôi nghĩ bạn có những gì bạn cần. Cố gắng không để thiết kế cho "chỉ trong trường hợp tôi cần nó sau này" và không sửa chữa những gì không bị hỏng. Nếu trong tương lai, bạn cần phải sử dụng Operation1 từ helper của bạn, sau đó thêm nó như là một phụ thuộc vào constructor (như bạn đã gợi ý), hoặc chỉ truyền nó cho phương thức mà bạn đang gọi. Nó sẽ phụ thuộc vào kịch bản, và bạn sẽ có nó khi bạn thực sự cần một cái gì đó.

EDIT: thay đổi "Cố gắng không thiết kế cho tương lai" vì nó không có vẻ như những gì tôi muốn nói.thay đổi

EDIT lại do trong câu hỏi

Bạn có thể để một cái gì đó như thế này:

helper.DoSomethingUsefulWith(foo); 

vì vậy phương pháp helper của bạn sẽ nhận được sự phụ thuộc nó cần để làm việc

+0

Cố gắng không để thiết kế cho tương lai? Tôi hiểu những gì bạn đang cố gắng nói, nhưng nói chung tôi không nghĩ đó là lời khuyên rất tốt. –

+0

Thiết kế nó để nó có thể được duy trì và mở rộng: CÓ !!! Thêm chức năng chỉ trong trường hợp nó sẽ là cần thiết sau: NO !!! đó là những gì tôi cố gắng nói :). Tôi nghĩ rằng tôi đã không chọn những từ tốt nhất cho nó hehe – ivowiblo

+0

@ivowiblo, tôi cập nhật câu hỏi của tôi bởi vì nó không thực sự là một "nếu" tình hình. Đây là một yêu cầu mà tôi đã gặp phải. Tôi cần phải có được rõ ràng hơn. Lấy làm tiếc. – Matt

3

thế nào về mối quan hệ ngẫu nhiên ("sử dụng"):

public class Helper : DefaultHelper 
{ 
    private readonly string customStuff; 

    public Helper(string value) 
    { 
     customStuff = value; 
    } 

    public override void DoSomethingHelpful(AbstractFoo foo) 
    { 
     foo.Operation1(); 
     Console.WriteLine("I was helpful using " + customStuff);   
    } 
} 

Vì vậy, bạn sửa đổi trình trợ giúp trừu tượng để mong đợi tham chiếu đến việc triển khai Foo thích hợp.

+1

Trong UML, đây là [Dependency] (http://publib.boulder.ibm.com/infocenter/rtnlhelp/v6r0m0/index.jsp?topic=%2Fcom.ibm.xtools.modeler.doc%2Ftopics%2Fcdepend. html) hoặc "sử dụng" mối quan hệ – SwDevMan81

+0

Vấn đề với điều này là Helper có cùng chữ ký được sử dụng bởi AbstractFoo và các lớp khác (tức là AbstractBoo). Nếu tôi thay đổi lớp cơ sở để chấp nhận foo, thì điều này không hữu ích khi tôi sử dụng một người trợ giúp từ boo, nếu điều đó có ý nghĩa. Boo có thể cần phải được thông qua thay vào đó (hoặc nó có thể được tốt là tham số-ít hơn). – Matt

+0

@Matt: Bạn có muốn 'BooHelper' có quyền truy cập vào' Boo' giống như cách bạn muốn 'FooHelper' có quyền truy cập vào' Foo' hay không? Nhận xét của bạn thực sự làm tôi bối rối. – ja72

1

Tôi nghĩ rằng tất cả các giải pháp của bạn đều tốt; họ chỉ cung cấp các khả năng khác nhau. Những khác biệt này không quan trọng quá nhiều nhưng có thể trong tương lai.

Bạn thích thứ hai, và bản năng của bạn là hướng dẫn tốt nhất ở đây, bạn biết nhiều hơn phần còn lại của chúng tôi về tương lai mã của bạn. Tôi thích giải pháp thứ hai của bạn tốt nhất chỉ vì nó loại bỏ một lớp và đơn giản hơn. Do sự đơn giản của nó, nếu bạn phải làm điều gì đó khác sau đó, bạn sẽ không phải vứt bỏ rất nhiều công việc.

Phương pháp đầu tiên cho phép bạn chơi trò chơi với các phiên bản và lớp con khác nhau của Trình trợ giúp (IHelper?). Phương pháp cuối cùng bổ sung thêm rất nhiều tính linh hoạt cho Trình trợ giúp. (Mặc dù nó có thể thêm rất nhiều bạn không cần Helper, chỉ là phương pháp bạn đang đi qua nó.) Bạn có thể chuyển sang sử dụng chúng sau này nếu một trong hai dường như để giải quyết thêm các vấn đề không phù hợp trong tương lai.

+0

+1 Tôi đồng ý cách tiếp cận giao diện và kết hợp Foo và trợ giúp có khả năng là tốt hơn trong ba và là một trong những tôi quyết định đi với. – Matt

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