Factory pattern trong C #: Làm thế nào để đảm bảo một cá thể đối tượng chỉ có thể được tạo bởi một lớp factory?


93

Gần đây tôi đã suy nghĩ về việc bảo mật một số mã của mình. Tôi tò mò làm cách nào để đảm bảo một đối tượng không bao giờ có thể được tạo trực tiếp mà chỉ thông qua một số phương thức của một lớp nhà máy. Giả sử tôi có một số lớp "đối tượng nghiệp vụ" và tôi muốn đảm bảo rằng bất kỳ phiên bản nào của lớp này sẽ có trạng thái bên trong hợp lệ. Để đạt được điều này, tôi sẽ cần thực hiện một số kiểm tra trước khi tạo một đối tượng, có thể là trong phương thức khởi tạo của nó. Tất cả đều ổn cho đến khi tôi quyết định rằng tôi muốn biến séc này trở thành một phần của logic nghiệp vụ. Vì vậy, làm cách nào để tôi có thể sắp xếp một đối tượng nghiệp vụ chỉ có thể được tạo thông qua một số phương thức trong lớp logic nghiệp vụ của tôi mà không bao giờ trực tiếp? Mong muốn tự nhiên đầu tiên là sử dụng từ khóa "bạn bè" cũ tốt của C ++ sẽ không thành công với C #. Vì vậy, chúng tôi cần các tùy chọn khác ...

Hãy thử một số ví dụ:

public MyBusinessObjectClass
{
    public string MyProperty { get; private set; }

    public MyBusinessObjectClass (string myProperty)
    {
        MyProperty = myProperty;
    }
}

public MyBusinessLogicClass
{
    public MyBusinessObjectClass CreateBusinessObject (string myProperty)
    {
        // Perform some check on myProperty

        if (true /* check is okay */)
            return new MyBusinessObjectClass (myProperty);

        return null;
    }
}

Tất cả đều ổn cho đến khi bạn nhớ rằng bạn vẫn có thể tạo phiên bản MyBusinessObjectClass trực tiếp mà không cần kiểm tra đầu vào. Tôi muốn loại trừ hoàn toàn khả năng kỹ thuật đó.

Vậy, cộng đồng nghĩ gì về điều này?


Phương thức MyBoClass có sai không?
pradeeptp

2
Tôi bối rối, tại sao bạn KHÔNG muốn để các đối tượng kinh doanh của bạn là những người chịu trách nhiệm bảo vệ sự bất biến của chính họ? Điểm của factory pattern (theo tôi hiểu) là A) giải quyết việc tạo một lớp phức tạp với nhiều phụ thuộc hoặc B) để factory là người quyết định loại thời gian chạy nào sẽ trả về, chỉ hiển thị một giao diện / trừu tượng cho khách hàng. Đưa các quy tắc kinh doanh của bạn vào các đối tượng kinh doanh của bạn là điều rất cần thiết của OOP.
sara

@kai Đúng, đối tượng nghiệp vụ chịu trách nhiệm bảo vệ nó bất biến, nhưng trình gọi hàm tạo phải đảm bảo rằng các đối số là hợp lệ trước khi chuyển chúng vào hàm tạo ! Mục đích của CreateBusinessObjectphương thức là xác thực các đối số VÀ để (trả về một đối tượng hợp lệ mới HOẶC trả về một lỗi) trong một lệnh gọi phương thức khi một người gọi không biết ngay lập tức các đối số có hợp lệ để truyền vào hàm tạo hay không.
Lightman

2
Tôi không đồng ý. Hàm tạo có trách nhiệm thực hiện bất kỳ kiểm tra bắt buộc nào đối với các tham số đã cho. Nếu tôi gọi một hàm tạo và không có ngoại lệ nào được ném ra, tôi tin tưởng rằng đối tượng được tạo ở trạng thái hợp lệ. Về mặt vật lý, không thể khởi tạo một lớp bằng cách sử dụng các đối số "sai". Tạo một Distancecá thể với một đối số phủ định sẽ đưa ra một ArgumentOutOfRangeExceptionví dụ, bạn sẽ chẳng thu được gì từ việc tạo một đối số DistanceFactorythực hiện việc kiểm tra tương tự. Tôi vẫn không thấy bạn phải đạt được gì ở đây.
sara

Câu trả lời:


55

Có vẻ như bạn chỉ muốn chạy một số logic nghiệp vụ trước khi tạo đối tượng - vậy tại sao bạn không tạo một phương thức tĩnh bên trong "BusinessClass" để thực hiện tất cả công việc kiểm tra "myProperty" bẩn thỉu và đặt hàm tạo là riêng tư?

public BusinessClass
{
    public string MyProperty { get; private set; }

    private BusinessClass()
    {
    }

    private BusinessClass(string myProperty)
    {
        MyProperty = myProperty;
    }

    public static BusinessClass CreateObject(string myProperty)
    {
        // Perform some check on myProperty

        if (/* all ok */)
            return new BusinessClass(myProperty);

        return null;
    }
}

Gọi nó sẽ khá đơn giản:

BusinessClass objBusiness = BusinessClass.CreateObject(someProperty);

6
À, bạn cũng có thể ném một ngoại lệ thay vì trả về null.
Ricardo Nolde

5
Không có ích gì khi có một phương thức khởi tạo mặc định riêng nếu bạn đã khai báo một phương thức tùy chỉnh. Ngoài ra, trong trường hợp này, thực sự không có gì để đạt được từ việc sử dụng một "phương thức khởi tạo" tĩnh thay vì chỉ thực hiện xác nhận trong phương thức khởi tạo thực (vì dù sao thì nó cũng là một phần của lớp). nó thậm chí là WORSE, vì bây giờ bạn có thể nhận được giá trị trả về null, mở ra cho NRE và "giá trị này có nghĩa là gì?" các câu hỏi.
sara

Có cách nào tốt để yêu cầu kiến ​​trúc này thông qua một lớp cơ sở Giao diện / trừu tượng không? tức là một lớp cơ sở giao diện / trừu tượng ra lệnh rằng người triển khai không được để lộ một ctor.
Arash Motamedi

1
@Arash Không. Các giao diện không thể định nghĩa các hàm tạo và một lớp trừu tượng xác định một hàm tạo được bảo vệ hoặc bên trong không thể ngăn các lớp kế thừa hiển thị nó thông qua một hàm tạo công khai của riêng nó.
Ricardo Nolde

66

Bạn có thể đặt phương thức khởi tạo là riêng tư và nhà máy là kiểu lồng nhau:

public class BusinessObject
{
    private BusinessObject(string property)
    {
    }

    public class Factory
    {
        public static BusinessObject CreateBusinessObject(string property)
        {
            return new BusinessObject(property);
        }
    }
}

Điều này hoạt động vì các kiểu lồng nhau có quyền truy cập vào các thành viên riêng tư của các kiểu bao quanh chúng. Tôi biết nó có một chút hạn chế, nhưng hy vọng nó sẽ giúp ...


Tôi đã nghĩ về điều đó nhưng nó chuyển những kiểm tra này vào chính đối tượng kinh doanh một cách hiệu quả, điều mà tôi đang cố gắng tránh.
Người dùng

@ so-tester: Bạn vẫn có thể kiểm tra trong nhà máy chứ không phải loại BusinessObject.
Jon Skeet

3
@JonSkeet Tôi biết câu hỏi này thực sự cũ, nhưng tôi tò mò về lợi ích của việc đặt CreateBusinessObjectphương thức bên trong một Factorylớp lồng nhau thay vì đặt phương thức tĩnh đó là một phương thức trực tiếp của BusinessObjectlớp ... bạn có thể nhớ lại động lực để làm như vậy?
Kiley Naro

3
@KileyNaro: Chà, đó là những gì câu hỏi đặt ra :) Nó không nhất thiết mang lại nhiều lợi thế, nhưng nó trả lời câu hỏi ... mặc dù vậy, có thể có những lúc điều này hữu ích - mô hình trình xây dựng xuất hiện trong tâm trí. (Trong trường hợp đó, người xây dựng sẽ là lớp lồng nhau, và nó sẽ có một ví dụ phương pháp gọi là Build.)
Jon Skeet

53

Hoặc, nếu bạn muốn thực sự ưa thích, hãy đảo ngược điều khiển: Yêu cầu lớp trả lại nhà máy và thiết lập nhà máy với một đại biểu có thể tạo lớp.

public class BusinessObject
{
  public static BusinessObjectFactory GetFactory()
  {
    return new BusinessObjectFactory (p => new BusinessObject (p));
  }

  private BusinessObject(string property)
  {
  }
}

public class BusinessObjectFactory
{
  private Func<string, BusinessObject> _ctorCaller;

  public BusinessObjectFactory (Func<string, BusinessObject> ctorCaller)
  {
    _ctorCaller = ctorCaller;
  }

  public BusinessObject CreateBusinessObject(string myProperty)
  {
    if (...)
      return _ctorCaller (myProperty);
    else
      return null;
  }
}

:)


Đoạn mã rất đẹp. Logic tạo đối tượng nằm trong một lớp riêng biệt và đối tượng chỉ có thể được tạo bằng cách sử dụng nhà máy.
Nikolai Samteladze

Mát mẻ. Có cách nào để bạn có thể giới hạn việc tạo BusinessObjectFactory cho BusinessObject.GetFactory tĩnh không?
Felix Keil

1
Ưu điểm của sự đảo ngược này là gì?
yatskovsky

1
@yatskovsky Đó chỉ là một cách để đảm bảo rằng đối tượng chỉ có thể được khởi tạo theo cách có kiểm soát trong khi vẫn có thể phân tích logic tạo thành một lớp chuyên dụng.
Fabian Schmied

15

Bạn có thể đặt hàm tạo trên lớp MyBusinessObjectClass của mình là nội bộ và di chuyển nó và nhà máy vào lắp ráp của riêng chúng. Bây giờ chỉ có nhà máy mới có thể tạo một thể hiện của lớp.


7

Ngoài những gì Jon đề xuất, bạn cũng có thể đặt phương thức gốc (bao gồm cả kiểm tra) là một phương thức tĩnh của BusinessObject ngay từ đầu. Sau đó, đặt phương thức khởi tạo là riêng tư và mọi người khác sẽ buộc phải sử dụng phương thức tĩnh.

public class BusinessObject
{
  public static Create (string myProperty)
  {
    if (...)
      return new BusinessObject (myProperty);
    else
      return null;
  }
}

Nhưng câu hỏi thực sự là - tại sao bạn có yêu cầu này? Có thể chấp nhận chuyển nhà máy hoặc phương thức nhà máy vào lớp không?


Đây hầu như không phải là một yêu cầu. Tôi chỉ muốn tách bạch rõ ràng đối tượng nghiệp vụ và logic. Cũng như không thích hợp để có những kiểm tra này trong mã của các trang, tôi cho rằng việc kiểm tra những kiểm tra này trong chính các đối tượng là không phù hợp. Vâng, có thể xác nhận cơ bản, nhưng không thực sự là các quy tắc kinh doanh.
Người dùng

Nếu bạn muốn tách logic và cấu trúc của một lớp chỉ cho xem xét thuận tiện, bạn luôn có thể sử dụng một lớp học phần và có cả trong các tập tin riêng biệt ...
Grx70

7

Sau rất nhiều năm, điều này đã được đặt ra, và tất cả các câu trả lời tôi thấy rất tiếc là cho bạn biết cách bạn nên thực hiện mã của mình thay vì đưa ra câu trả lời thẳng thắn. Câu trả lời thực tế mà bạn đang tìm kiếm là có các lớp của bạn với một phương thức khởi tạo riêng nhưng là một trình khởi tạo công khai, có nghĩa là bạn chỉ có thể tạo các bản sao mới từ các bản sao hiện có khác ... chỉ có sẵn trong nhà máy:

Giao diện cho các lớp học của bạn:

public interface FactoryObject
{
    FactoryObject Instantiate();
}

Lớp của bạn:

public class YourClass : FactoryObject
{
    static YourClass()
    {
        Factory.RegisterType(new YourClass());
    }

    private YourClass() {}

    FactoryObject FactoryObject.Instantiate()
    {
        return new YourClass();
    }
}

Và, cuối cùng, nhà máy:

public static class Factory
{
    private static List<FactoryObject> knownObjects = new List<FactoryObject>();

    public static void RegisterType(FactoryObject obj)
    {
        knownObjects.Add(obj);
    }

    public static T Instantiate<T>() where T : FactoryObject
    {
        var knownObject = knownObjects.Where(x => x.GetType() == typeof(T));
        return (T)knownObject.Instantiate();
    }
}

Sau đó, bạn có thể dễ dàng sửa đổi mã này nếu bạn cần các tham số bổ sung cho việc khởi tạo hoặc để xử lý trước các phiên bản bạn tạo. Và mã này sẽ cho phép bạn ép buộc việc khởi tạo thông qua nhà máy vì hàm tạo lớp là riêng tư.


4

Tuy nhiên, một tùy chọn khác (nhẹ) là tạo một phương thức nhà máy tĩnh trong lớp BusinessObject và giữ phương thức khởi tạo ở chế độ riêng tư.

public class BusinessObject
{
    public static BusinessObject NewBusinessObject(string property)
    {
        return new BusinessObject();
    }

    private BusinessObject()
    {
    }
}

2

Vì vậy, có vẻ như những gì tôi muốn không thể được thực hiện một cách "trong sáng". Nó luôn luôn là một số loại "gọi lại" cho lớp logic.

Có lẽ tôi có thể làm điều đó theo cách đơn giản, chỉ cần thực hiện một phương thức contructor trong lớp đối tượng trước tiên gọi lớp logic để kiểm tra đầu vào?

public MyBusinessObjectClass
{
    public string MyProperty { get; private set; }

    private MyBusinessObjectClass (string myProperty)
    {
        MyProperty  = myProperty;
    }

    pubilc static MyBusinessObjectClass CreateInstance (string myProperty)
    {
        if (MyBusinessLogicClass.ValidateBusinessObject (myProperty)) return new MyBusinessObjectClass (myProperty);

        return null;
    }
}

public MyBusinessLogicClass
{
    public static bool ValidateBusinessObject (string myProperty)
    {
        // Perform some check on myProperty

        return CheckResult;
    }
}

Bằng cách này, đối tượng kinh doanh không thể được tạo trực tiếp và phương pháp kiểm tra công khai trong logic kinh doanh cũng sẽ không gây hại.


2

Trong trường hợp tách biệt tốt giữa các giao diện và triển khai,
mẫu bộ khởi tạo được bảo vệ-cấu tạo-công cộng cho phép một giải pháp rất gọn gàng.

Cho một đối tượng kinh doanh:

public interface IBusinessObject { }

class BusinessObject : IBusinessObject
{
    public static IBusinessObject New() 
    {
        return new BusinessObject();
    }

    protected BusinessObject() 
    { ... }
}

và một nhà máy kinh doanh:

public interface IBusinessFactory { }

class BusinessFactory : IBusinessFactory
{
    public static IBusinessFactory New() 
    {
        return new BusinessFactory();
    }

    protected BusinessFactory() 
    { ... }
}

thay đổi sau đối với trình BusinessObject.New()khởi tạo đưa ra giải pháp:

class BusinessObject : IBusinessObject
{
    public static IBusinessObject New(BusinessFactory factory) 
    { ... }

    ...
}

Ở đây cần tham chiếu đến nhà máy kinh doanh bê tông để gọi trình BusinessObject.New()khởi tạo. Nhưng người duy nhất có tài liệu tham khảo được yêu cầu là chính nhà máy kinh doanh.

Chúng tôi đã nhận những gì chúng ta muốn: là người duy nhất có thể tạo ra BusinessObjectBusinessFactory.


Vì vậy, bạn có đảm bảo rằng phương thức khởi tạo công cộng hoạt động duy nhất của đối tượng yêu cầu Factory và tham số Factory cần thiết đó chỉ có thể được khởi tạo bằng cách gọi phương thức static của Factory? Có vẻ như đây là một giải pháp hiệu quả, nhưng tôi có cảm giác rằng các đoạn mã như thế public static IBusinessObject New(BusinessFactory factory) sẽ khiến nhiều người phải nhíu mày nếu ai đó duy trì mã.
Flater

@Flater Đồng ý. Tôi sẽ thay đổi tên tham số factorythành asFriend. Trong cơ sở mã của tôi nó sẽ hiển thị sau đó làpublic static IBusinessObject New(BusinessFactory asFriend)
Reuven Bass

Tôi không hiểu gì cả. Làm thế nào nó hoạt động? Nhà máy không thể tạo các phiên bản mới của IBusinessObject cũng như không có ý định (phương pháp) làm như vậy ...?
lapsus

2
    public class HandlerFactory: Handler
    {
        public IHandler GetHandler()
        {
            return base.CreateMe();
        }
    }

    public interface IHandler
    {
        void DoWork();
    }

    public class Handler : IHandler
    {
        public void DoWork()
        {
            Console.WriteLine("hander doing work");
        }

        protected IHandler CreateMe()
        {
            return new Handler();
        }

        protected Handler(){}
    }

    public static void Main(string[] args)
    {
        // Handler handler = new Handler();         - this will error out!
        var factory = new HandlerFactory();
        var handler = factory.GetHandler();

        handler.DoWork();           // this works!
    }

Vui lòng thử cách tiếp cận của tôi, 'factory' là vùng chứa của trình xử lý và chỉ nó mới có thể tạo một thể hiện cho chính nó. với một số sửa đổi, nó có thể phù hợp với nhu cầu của bạn.
lin

1
Trong ví dụ của bạn, điều sau sẽ không khả thi (điều này sẽ không hợp lý) ?: factory.DoWork ();
Whyser

1

Tôi đã đặt nhà máy trong cùng một assembly với lớp miền và đánh dấu phương thức khởi tạo của lớp miền bên trong. Bằng cách này, bất kỳ lớp nào trong miền của bạn cũng có thể tạo một phiên bản, nhưng bạn tin tưởng mình thì không, phải không? Bất kỳ ai viết mã bên ngoài lớp miền sẽ phải sử dụng nhà máy của bạn.

public class Person
{
  internal Person()
  {
  }
}

public class PersonFactory
{
  public Person Create()
  {
    return new Person();
  }  
}

Tuy nhiên, tôi phải đặt câu hỏi về cách tiếp cận của bạn :-)

Tôi nghĩ rằng nếu bạn muốn lớp Person của mình hợp lệ khi tạo, bạn phải đặt mã vào hàm tạo.

public class Person
{
  public Person(string firstName, string lastName)
  {
    FirstName = firstName;
    LastName = lastName;
    Validate();
  }
}

1

Giải pháp này được dựa trên munificents ý tưởng của việc sử dụng một mã thông báo trong constructor. Hoàn tất trong câu trả lời này, hãy đảm bảo rằng đối tượng chỉ được tạo bởi nhà máy (C #)

  public class BusinessObject
    {
        public BusinessObject(object instantiator)
        {
            if (instantiator.GetType() != typeof(Factory))
                throw new ArgumentException("Instantiator class must be Factory");
        }

    }

    public class Factory
    {
        public BusinessObject CreateBusinessObject()
        {
            return new BusinessObject(this);
        }
    }

1

Nhiều cách tiếp cận với sự cân bằng khác nhau đã được đề cập.

  • Việc lồng lớp nhà máy vào lớp do tư nhân xây dựng chỉ cho phép nhà máy xây dựng 1 lớp. Tại thời điểm đó, bạn tốt hơn vớiCreate phương pháp và một ctor riêng.
  • Sử dụng kế thừa và ctor được bảo vệ có cùng một vấn đề.

Tôi muốn đề xuất factory như một lớp từng phần chứa các lớp lồng nhau riêng với các hàm tạo công khai. Bạn đang ẩn 100% đối tượng mà nhà máy của bạn đang xây dựng và chỉ hiển thị những gì bạn chọn thông qua một hoặc nhiều giao diện.

Trường hợp sử dụng mà tôi đã nghe cho điều này là khi bạn muốn theo dõi 100% các phiên bản trong nhà máy. Thiết kế này đảm bảo không ai ngoài nhà máy có quyền truy cập vào việc tạo ra các phiên bản "hóa chất" được xác định trong "nhà máy" và nó loại bỏ nhu cầu lắp ráp riêng biệt để đạt được điều đó.

== ChemicalFactory.cs ==
partial class ChemicalFactory {
    private  ChemicalFactory() {}

    public interface IChemical {
        int AtomicNumber { get; }
    }

    public static IChemical CreateOxygen() {
        return new Oxygen();
    }
}


== Oxygen.cs ==
partial class ChemicalFactory {
    private class Oxygen : IChemical {
        public Oxygen() {
            AtomicNumber = 8;
        }
        public int AtomicNumber { get; }
    }
}



== Program.cs ==
class Program {
    static void Main(string[] args) {
        var ox = ChemicalFactory.CreateOxygen();
        Console.WriteLine(ox.AtomicNumber);
    }
}

0

Tôi không hiểu tại sao bạn lại muốn tách "logic kinh doanh" khỏi "đối tượng kinh doanh". Điều này nghe có vẻ như là một sự biến dạng của hướng đối tượng và cuối cùng bạn sẽ tự trói mình trong các nút thắt bằng cách thực hiện phương pháp đó.


Tôi cũng không hiểu sự phân biệt này. Ai đó có thể giải thích tại sao điều này đang được thực hiện, và mã nào sẽ nằm trong đối tượng kinh doanh?
pipTheGeek

Đó chỉ là một cách tiếp cận có thể được sử dụng. Tôi muốn đối tượng kinh doanh chủ yếu là cấu trúc dữ liệu nhưng không phải với tất cả các trường đều mở để đọc / ghi. Nhưng câu hỏi thực sự là về việc có thể khởi tạo một đối tượng chỉ với sự trợ giúp của phương pháp factory chứ không phải trực tiếp.
Người dùng

@Jim: Cá nhân tôi đồng ý với quan điểm của bạn, nhưng về mặt chuyên môn, điều này được thực hiện liên tục. Dự án hiện tại của tôi là một dịch vụ web lấy một chuỗi HTML, dọn dẹp nó theo một số quy tắc đặt trước, sau đó trả về chuỗi đã được làm sạch. Tôi được yêu cầu phải trải qua 7 lớp mã của riêng mình "bởi vì tất cả các dự án của chúng tôi phải được xây dựng từ cùng một mẫu dự án". Đó là lý do mà hầu hết mọi người hỏi những câu hỏi này trên SO không có quyền tự do thay đổi toàn bộ luồng mã của họ.
Flater

0

Tôi không nghĩ có giải pháp nào không tệ hơn vấn đề này, tất cả những gì anh ấy ở trên yêu cầu một nhà máy tĩnh công cộng mà IMHO là một vấn đề tồi tệ hơn và sẽ không ngăn mọi người chỉ gọi nhà máy sử dụng đối tượng của bạn - nó không che giấu bất cứ điều gì. Tốt nhất nên để lộ một giao diện và / hoặc giữ hàm tạo như bên trong nếu bạn có thể, đó là cách bảo vệ tốt nhất vì hợp ngữ là mã đáng tin cậy.

Một tùy chọn là có một phương thức khởi tạo tĩnh đăng ký một nhà máy ở đâu đó với một cái gì đó giống như một IOC container.


0

Đây là một giải pháp khác cho "chỉ vì bạn có thể không có nghĩa là bạn nên làm" ...

Nó đáp ứng các yêu cầu giữ riêng phương thức khởi tạo đối tượng nghiệp vụ và đặt logic nhà máy trong một lớp khác. Sau đó, nó có một chút sơ sài.

Lớp factory có một phương thức tĩnh để tạo các đối tượng nghiệp vụ. Nó bắt nguồn từ lớp đối tượng nghiệp vụ để truy cập một phương thức xây dựng được bảo vệ tĩnh gọi hàm tạo riêng.

Nhà máy là trừu tượng nên bạn không thể thực sự tạo một thể hiện của nó (vì nó cũng sẽ là một đối tượng nghiệp vụ, vì vậy điều đó sẽ kỳ lạ) và nó có một phương thức khởi tạo riêng nên mã máy khách không thể lấy từ nó.

Điều không bị ngăn cản là mã khách hàng cũng bắt nguồn từ lớp đối tượng nghiệp vụ và gọi phương thức xây dựng tĩnh được bảo vệ (nhưng chưa được xác thực). Hoặc tệ hơn, việc gọi hàm tạo mặc định được bảo vệ mà chúng ta phải thêm vào để có được lớp nhà máy để biên dịch ngay từ đầu. (Tình cờ có thể là vấn đề với bất kỳ mẫu nào tách lớp factory khỏi lớp đối tượng nghiệp vụ.)

Tôi không cố gắng gợi ý bất cứ ai có suy nghĩ đúng đắn của họ nên làm điều gì đó như thế này, nhưng đó là một bài tập thú vị. FWIW, giải pháp ưa thích của tôi là sử dụng một hàm tạo bên trong và ranh giới lắp ráp làm lớp bảo vệ.

using System;

public class MyBusinessObjectClass
{
    public string MyProperty { get; private set; }

    private MyBusinessObjectClass(string myProperty)
    {
        MyProperty = myProperty;
    }

    // Need accesible default constructor, or else MyBusinessObjectFactory declaration will generate:
    // error CS0122: 'MyBusinessObjectClass.MyBusinessObjectClass(string)' is inaccessible due to its protection level
    protected MyBusinessObjectClass()
    {
    }

    protected static MyBusinessObjectClass Construct(string myProperty)
    {
        return new MyBusinessObjectClass(myProperty);
    }
}

public abstract class MyBusinessObjectFactory : MyBusinessObjectClass
{
    public static MyBusinessObjectClass CreateBusinessObject(string myProperty)
    {
        // Perform some check on myProperty

        if (true /* check is okay */)
            return Construct(myProperty);

        return null;
    }

    private MyBusinessObjectFactory()
    {
    }
}

0

Sẽ đánh giá cao khi nghe một số suy nghĩ về giải pháp này. Người duy nhất có thể tạo 'MyClassPrivilegeKey' là nhà máy. và 'MyClass' yêu cầu nó trong hàm tạo. Do đó tránh được phản ánh về nhà thầu tư nhân / "đăng ký" vào nhà máy.

public static class Runnable
{
    public static void Run()
    {
        MyClass myClass = MyClassPrivilegeKey.MyClassFactory.GetInstance();
    }
}

public abstract class MyClass
{
    public MyClass(MyClassPrivilegeKey key) { }
}

public class MyClassA : MyClass
{
    public MyClassA(MyClassPrivilegeKey key) : base(key) { }
}

public class MyClassB : MyClass
{
    public MyClassB(MyClassPrivilegeKey key) : base(key) { }
}


public class MyClassPrivilegeKey
{
    private MyClassPrivilegeKey()
    {
    }

    public static class MyClassFactory
    {
        private static MyClassPrivilegeKey key = new MyClassPrivilegeKey();

        public static MyClass GetInstance()
        {
            if (/* some things == */true)
            {
                return new MyClassA(key);
            }
            else
            {
                return new MyClassB(key);
            }
        }
    }
}
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.