C#中的工厂模式:如何确保对象实例只能由工厂类创build?
最近我一直在考虑保护我的一些代码。 我很好奇如何确保一个对象不能直接创build,但只能通过工厂类的一些方法。 让我们说我有一些“业务对象”类,我想确保这个类的任何实例将有一个有效的内部状态。 为了实现这一点,我需要在创build一个对象之前执行一些检查,可能是在它的构造函数中。 这一切都可以,直到我决定让这个检查成为业务逻辑的一部分。 那么,我怎样才能安排一个业务对象,只能通过业务逻辑类中的某种方法创build,而不能直接创build? 使用C ++的旧“好友”关键字的第一个自然愿望将与C#不兼容。 所以我们需要其他的select
我们来看一个例子:
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; } }
直到你记得你仍然可以直接创buildMyBusinessObjectClass实例,没有检查input,这一切都没问题。 我想完全排除这种技术可能性。
那么,社区对此有何看法呢?
看起来你只是想在创build对象之前运行一些业务逻辑 – 那么为什么不只是在“BusinessClass”内创build一个静态方法来完成所有肮脏的“myProperty”检查工作,并使构造函数是私有的?
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; } }
调用它会非常简单:
BusinessClass objBusiness = BusinessClass.CreateObject(someProperty);
你可以使构造函数是私有的,工厂是嵌套的types:
public class BusinessObject { private BusinessObject(string property) { } public class Factory { public static BusinessObject CreateBusinessObject(string property) { return new BusinessObject(property); } } }
这是可行的,因为嵌套types可以访问其封闭types的私有成员。 我知道这是有点限制,但希望它会帮助…
或者,如果你想要真的看上去,反转控制:让class级返回工厂,然后用一个可以创buildclass级的代表来组织工厂。
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; } }
🙂
你可以使MyBusinessObjectClass类的构造函数在内部,并将它和工厂移动到它们自己的程序集中。 现在只有工厂应该能够构build一个类的实例。
除了Jon所build议的,你也可以让工厂方法(包括检查)成为BusinessObject的一个静态方法。 然后,构造函数是私有的,其他人将被迫使用静态方法。
public class BusinessObject { public static Create (string myProperty) { if (...) return new BusinessObject (myProperty); else return null; } }
但真正的问题是 – 为什么你有这个要求? 将工厂或工厂方法转移到class级中是可以接受的吗?
另一个(轻量级)选项是在BusinessObject类中创build静态工厂方法,并使构造函数保持私有状态。
public class BusinessObject { public static BusinessObject NewBusinessObject(string property) { return new BusinessObject(); } private BusinessObject() { } }
所以,看起来我想要的东西不能以“纯粹”的方式来完成。 它总是某种“callback”逻辑类。
也许我可以做一个简单的方法,只需在对象类中调用逻辑类来检查input的构造方法?
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; } }
这样,业务对象不能直接创build,业务逻辑中的公开检查方法也不会有什么坏处。
在接口和实现之间有很好的分离的情况下
protected-constructor-public-initializer模式允许一个非常整洁的解决scheme。
给定一个业务对象:
public interface IBusinessObject { } class BusinessObject : IBusinessObject { public static IBusinessObject New() { return new BusinessObject(); } protected BusinessObject() { ... } }
和一个商业工厂:
public interface IBusinessFactory { } class BusinessFactory : IBusinessFactory { public static IBusinessFactory New() { return new BusinessFactory(); } protected BusinessFactory() { ... } }
以下更改为BusinessObject.New()
初始化程序给出了解决scheme:
class BusinessObject : IBusinessObject { public static IBusinessObject New(BusinessFactory factory) { ... } ... }
这里需要调用BusinessObject.New()
初始化程序来引用具体的业务工厂。 但唯一具有所需参照的是商业工厂本身。
我们得到了我们想要的:唯一可以创buildBusinessObject
是BusinessFactory
。
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! }
我把这个工厂放在和domain类相同的程序集中,并把domain类的构造函数标记为internal。 这样,你的域中的任何类都可以创build一个实例,但是你相信自己不会这样做,对吗? 任何在域图层之外编写代码的人都必须使用你的工厂。
public class Person { internal Person() { } } public class PersonFactory { public Person Create() { return new Person(); } }
但是,我必须质疑你的做法:-)
我认为如果你希望你的Person类在创build时有效,你必须把代码放在构造函数中。
public class Person { public Person(string firstName, string lastName) { FirstName = firstName; LastName = lastName; Validate(); } }
这个解决scheme是基于在构造函数中使用令牌的想法。 完成这个答案确保对象只由工厂(C#)创build
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); } }
已经提到了具有不同折衷的多种方法。
- 将工厂类嵌套在私人build造的类中只允许工厂构build一个类。 在这一点上,你最好用
Create
方法和私人ctor。 - 使用inheritance和受保护的ctor具有相同的问题。
我想build议工厂作为包含私有嵌套类与公共构造函数的部分类。 你100%隐藏了工厂正在构build的对象,只通过一个或多个接口公开你select的对象。
我所听到的用例是当你想跟踪工厂中100%的实例时。 这种devise保证没有人能够在工厂中创build“化学品”的实例,而且不需要单独的组装来实现这一点。
== 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); } }
我不明白你为什么要把“业务逻辑”与“业务对象”分开。 这听起来像是一个面向对象的扭曲,你会通过采取这种方法结束自己的结。
我不认为有一个解决scheme不比问题更糟糕,他所有的上面都需要一个公共的静态工厂,恕我直言是一个更糟糕的问题,不会阻止人们只是叫工厂使用你的对象 – 它不隐藏任何东西。 最好公开一个接口和/或保持构造函数为内部的,如果可以的话,这是程序集是受信任的代码的最好的保护。
一种select是有一个静态的构造函数,在某个地方注册一个像IOC容器的工厂。
这里是另一个解决scheme的“只是因为你可以不意味着你应该”…
它确实符合保持业务对象构造函数私有并将工厂逻辑放在另一个类中的要求。 之后,它有点粗略。
工厂类具有创build业务对象的静态方法。 它来自业务对象类,以访问调用私有构造函数的静态保护构造方法。
工厂是抽象的,所以你不能真正创build它的一个实例(因为它也是一个业务对象,所以这将是奇怪的),它有一个私人的构造函数,因此客户端代码不能从它派生。
不能阻止的是客户端代码也从业务对象类派生,并调用受保护的(但未经validation的)静态构造方法。 或者更糟的是,调用受保护的默认构造函数,我们不得不添加工厂类来编译。 (顺便说一句,可能是任何将工厂类与业务对象类分开的模式的问题。)
我并不是想让任何一个正确的人都这样做,但这是一个有趣的练习。 FWIW,我的首选解决scheme是使用内部构造函数和assembly边界作为警卫。
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() { } }