Lớp cơ sở làm nhà máy?


14

Tôi đã viết một số mã vào cuối tuần và tôi thấy mình muốn viết một nhà máy như một phương thức tĩnh trong một lớp cơ sở.

Câu hỏi của tôi chỉ đơn giản là để biết nếu đây là cách tiếp cận ac # idomatic?

Tôi cảm thấy rằng nó có thể không xuất phát từ thực tế là lớp cơ sở có kiến ​​thức về lớp dẫn xuất.

Điều đó nói rằng, tôi không chắc chắn về một cách đơn giản hơn để có được kết quả tương tự. Cả một lớp nhà máy khác dường như (ít nhất là với tôi) giống như sự phức tạp không cần thiết (?)

Cái gì đó như:

class Animal
{
  public static Animal CreateAnimal(string name)
  {
     switch(name)
     {
        case "Shark":
          return new SeaAnimal();
          break;
        case "Dog":
          return new LandAnimal();
          break;
        default:
          throw new Exception("unknown animal");
     }
  }
}
class LandAnimal : Animal
{
}

class SeaAnimal : Animal
{
}

Làm thế nào bạn sẽ kiểm tra các nhà máy của bạn?

với mã trong câu hỏi này, tôi sẽ không. nhưng câu trả lời đã giúp tôi đi theo con đường tích hợp nhiều thử nghiệm hơn vào phong cách mã hóa của mình
Aaron Anodide

Hãy xem xét rằng việc kéo cả Seaworld và Landworld vào lớp Thú của bạn, tất cả đều khiến việc xử lý trong một môi trường biệt lập trở nên khó khăn hơn.

Câu trả lời:


13

Chà, lợi thế của một lớp nhà máy riêng biệt là nó có thể bị chế giễu trong các bài kiểm tra đơn vị.

Nhưng nếu bạn sẽ không làm điều đó, hoặc biến nó thành đa hình theo bất kỳ cách nào khác, thì một phương thức Factory tĩnh trên chính lớp đó là OK.


cảm ơn, bạn có thể đưa ra một ví dụ về những gì bạn đang đề cập đến khi bạn nói đa hình theo bất kỳ cách nào khác không?
Aaron Anodide

Tôi có nghĩa là nếu bạn muốn có thể thay thế một phương pháp nhà máy bằng một phương pháp khác. Mocking nó cho mục đích thử nghiệm chỉ là một trong những ví dụ phổ biến nhất về điều đó.
pdr

18

Bạn có thể sử dụng Generics để tránh câu lệnh chuyển đổi và tách riêng các triển khai hạ nguồn từ lớp cơ sở:

 public static T CreateAnimal<T>() where T: new, Animal
 {
    return new T();
 }

Sử dụng:

LandAnimal rabbit = Animal.CreateAnimal();  //Type inference should should just figure out the T by the return indicated.

hoặc là

 var rabbit = Animal.CreateAnimal<LandAnimal>(); 


2
@PeterK. Fowler đề cập đến các câu lệnh chuyển đổi trong Code Smells, nhưng giải pháp của anh ta là chúng nên được trích xuất sang các lớp Factory, thay vì được thay thế. Điều đó nói rằng, nếu bạn luôn biết loại hình tại thời điểm phát triển, thuốc generic là một giải pháp thậm chí tốt hơn. Nhưng nếu bạn không (như ví dụ ban đầu ngụ ý) thì tôi sẽ lập luận rằng sử dụng sự phản chiếu để tránh chuyển đổi trong phương thức nhà máy có thể là một nền kinh tế sai lầm.
pdr

câu lệnh chuyển đổi và chuỗi if-if-if giống nhau. tôi sử dụng trước vì kiểm tra thời gian biên dịch buộc phải xử lý trường hợp mặc định
Aaron Anodide

4
@PeterK, trong một câu lệnh chuyển đổi, mã nằm ở một nơi duy nhất. Trong OO toàn diện, cùng một mã có thể được trải rộng trên nhiều lớp riêng biệt. Có một khu vực màu xám nơi sự phức tạp được thêm vào của việc có nhiều đoạn trích ít được mong muốn hơn một câu lệnh chuyển đổi.

@Thorbjorn, tôi đã có suy nghĩ chính xác đó, nhưng chưa bao giờ ngắn gọn như vậy, cảm ơn vì đã nói thành lời
Aaron Anodide

5

Được đưa đến cùng cực, nhà máy cũng có thể là chung chung.

interface IFactory<K, T> where K : IComparable
{
    T Create(K key);
}

Sau đó, người ta có thể tạo ra bất kỳ loại nhà máy của các đối tượng, từ đó có thể tạo ra bất kỳ loại đối tượng. Tôi không chắc nếu điều đó đơn giản hơn, nó chắc chắn là chung chung hơn.

Tôi không thấy bất cứ điều gì sai với một tuyên bố chuyển đổi cho một triển khai nhà máy nhỏ. Khi bạn đã vào một số lượng lớn các đối tượng hoặc có thể phân cấp các đối tượng lớp khác nhau, tôi nghĩ rằng một cách tiếp cận chung hơn là phù hợp hơn.


1

Ý kiến ​​tồi. Đầu tiên, nó vi phạm nguyên tắc đóng mở. Đối với bất kỳ động vật mới nào, bạn sẽ phải gặp lại lớp cơ sở của mình và bạn có khả năng sẽ phá vỡ nó. Các phụ thuộc sẽ đi sai đường.

Nếu bạn cần tạo động vật từ một cấu hình, một cấu trúc như thế này sẽ ổn, mặc dù sử dụng sự phản chiếu để có được loại phù hợp với tên và khởi tạo nó bằng cách sử dụng thông tin loại thu được sẽ là một lựa chọn tốt hơn.

Nhưng dù sao bạn cũng nên tạo một lớp nhà máy chuyên dụng, cởi trói khỏi hệ thống phân cấp lớp động vật và trả về giao diện IAnimal thay vì kiểu cơ sở. Sau đó, nó sẽ trở nên hữu ích, bạn sẽ đạt được một số tách.


0

Câu hỏi này là kịp thời cho tôi - tôi đã viết gần như chính xác mã như vậy ngày hôm qua. Chỉ cần thay thế "Động vật" bằng bất cứ điều gì có liên quan trong dự án của tôi, mặc dù tôi sẽ gắn bó với "Động vật" để thảo luận ở đây. Thay vì một câu lệnh 'chuyển đổi', tôi đã có một loạt các câu lệnh 'nếu' phức tạp hơn, liên quan đến nhiều hơn là chỉ so sánh một biến với các giá trị cố định nhất định. Nhưng đó là một chi tiết. Một phương pháp nhà máy tĩnh có vẻ như là một cách gọn gàng để thiết kế mọi thứ, vì thiết kế đã xuất hiện từ việc tái cấu trúc các mớ hỗn độn mã nhanh và bẩn trước đó.

Tôi đã từ chối thiết kế này với lý do lớp cơ sở có kiến ​​thức về lớp dẫn xuất. Nếu các lớp LandAnimal và SeaAnimal nhỏ, gọn gàng và dễ dàng, chúng có thể nằm trong cùng một tệp nguồn. Nhưng tôi có các phương thức lộn xộn lớn để đọc các tệp văn bản không tuân theo bất kỳ tiêu chuẩn được xác định chính thức nào - tôi muốn lớp LandAnimal của mình trong tệp nguồn riêng của nó.

Điều đó dẫn đến sự phụ thuộc tệp vòng tròn - LandAnimal có nguồn gốc từ Động vật, nhưng Động vật cần phải biết rằng LandAnimal, SeaAnimal và mười lăm lớp khác tồn tại. Tôi đã rút phương thức xuất xưởng ra, đưa nó vào tệp riêng của mình (trong ứng dụng chính của tôi chứ không phải thư viện Động vật của tôi) Có một phương pháp tĩnh nhà máy có vẻ dễ thương và thông minh, nhưng tôi nhận ra rằng nó không thực sự giải quyết bất kỳ vấn đề thiết kế nào.

Tôi không biết làm thế nào điều này liên quan đến C # thành ngữ, vì tôi chuyển đổi ngôn ngữ rất nhiều, tôi thường bỏ qua các thành ngữ và quy ước đặc biệt với các ngôn ngữ và ngăn xếp bên ngoài công việc thông thường của tôi. Nếu bất cứ điều gì C # của tôi có thể trông "pythonic" nếu điều đó có ý nghĩa. Tôi nhắm đến sự rõ ràng chung.

Ngoài ra, tôi không biết liệu có lợi thế gì khi sử dụng phương thức tĩnh nhà máy trong trường hợp các lớp nhỏ, đơn giản không có khả năng được mở rộng trong công việc trong tương lai - trong một số trường hợp có tất cả trong một tệp nguồn.

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.