C#中的工厂模式:如何确保对象实例只能由工厂类创建?
最近我一直在考虑保护我的一些代码。我很好奇,如何确保对象永远不能直接创建,而只能通过工厂类的某些方法创建。假设我有一个“业务对象”类,我想确保这个类的任何实例都有一个有效的内部状态。为了实现这一点,我需要在创建对象之前执行一些检查,可能是在其构造函数中。在我决定将此检查作为业务逻辑的一部分之前,这一切都没有问题。那么,如何安排业务对象只能通过业务逻辑类中的某些方法创建,而不能直接创建?使用C++中的“老朋友”关键字的第一个自然愿望将与C语言相去甚远。所以我们需要其他的选择 让我们来举个例子:C#中的工厂模式:如何确保对象实例只能由工厂类创建?,c#,design-patterns,oop,factory,C#,Design Patterns,Oop,Factory,最近我一直在考虑保护我的一些代码。我很好奇,如何确保对象永远不能直接创建,而只能通过工厂类的某些方法创建。假设我有一个“业务对象”类,我想确保这个类的任何实例都有一个有效的内部状态。为了实现这一点,我需要在创建对象之前执行一些检查,可能是在其构造函数中。在我决定将此检查作为业务逻辑的一部分之前,这一切都没有问题。那么,如何安排业务对象只能通过业务逻辑类中的某些方法创建,而不能直接创建?使用C++中的“老朋友”关键字的第一个自然愿望将与C语言相去甚远。所以我们需要其他的选择 让我们来举个例子: p
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;
}
}
在您记住您仍然可以直接创建MyBusinessObjectClass实例之前,一切都没问题,无需检查输入。我想完全排除这种技术可能性
那么,社区对此有何看法?您可以将MyBusinessObjectClass类上的构造函数设置为内部构造函数,并将其和工厂移动到它们自己的程序集中。现在,应该只有工厂能够构造类的实例。您可以将构造函数设为私有,并将工厂设为嵌套类型:
public class BusinessObject
{
private BusinessObject(string property)
{
}
public class Factory
{
public static BusinessObject CreateBusinessObject(string property)
{
return new BusinessObject(property);
}
}
}
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;
}
}
这是因为嵌套类型可以访问其封闭类型的私有成员。我知道这有点限制,但希望它能有所帮助……除了Jon建议的方法外,您还可以首先让factory方法(包括检查)成为BusinessObject的静态方法。然后,让构造函数私有,其他所有人都将被迫使用静态方法
public class BusinessObject
{
public static Create (string myProperty)
{
if (...)
return new BusinessObject (myProperty);
else
return null;
}
}
但真正的问题是——你为什么有这个要求?将工厂或工厂方法移动到类中是否可以接受?另一个(轻量级)选项是在BusinessObject类中创建静态工厂方法并保持构造函数私有
public class BusinessObject
{
public static BusinessObject NewBusinessObject(string property)
{
return new BusinessObject();
}
private BusinessObject()
{
}
}
或者,如果你真的想做的更好,倒过来控制:让类返回工厂,并用一个可以创建类的委托来指示工厂
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;
}
}
公共类BusinessObject
{
公共静态BusinessObjectFactory GetFactory()
{
返回新的BusinessObjectFactory(p=>newbusinessobject(p));
}
私有BusinessObject(字符串属性)
{
}
}
公共类BusinessObjectFactory
{
私有函数调用方;
public BusinessObjectFactory(函数调用方)
{
_ctorCaller=ctorCaller;
}
public BusinessObject CreateBusinessObject(字符串myProperty)
{
如果(…)
返回调用方(myProperty);
其他的
返回null;
}
}
:)所以,看起来我想要的东西不能用“纯粹”的方式来完成。它总是对逻辑类进行某种“回调” 也许我可以用一种简单的方法来做,只是让object类中的constructor方法首先调用logic类来检查输入
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;
}
}
这样,业务对象就不能直接创建,业务逻辑中的公共检查方法也不会有什么坏处。我不明白为什么要将“业务逻辑”与“业务对象”分开。这听起来像是对面向对象的一种扭曲,采用这种方法最终会使自己陷入困境。我将工厂与域类放在同一个程序集中,并将域类的构造函数标记为内部。这样,域中的任何类都可以创建实例,但您相信自己不会,对吗?任何在域层之外编写代码的人都必须使用您的工厂
public class Person
{
internal Person()
{
}
}
public class PersonFactory
{
public Person Create()
{
return new Person();
}
}
然而,我必须质疑你的方法:-)
我认为,如果希望Person类在创建时有效,那么必须将代码放入构造函数中
public class Person
{
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
Validate();
}
}
看起来您只是想在创建对象之前运行一些业务逻辑,那么为什么不在“BusinessClass”中创建一个静态方法来执行所有脏的“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);
在接口和实现之间良好分离的情况下,
受保护的构造函数公共初始值设定项模式允许一个非常简洁的解决方案 给定一个业务对象:
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()
初始值设定项的以下更改提供了解决方案:
class BusinessObject : IBusinessObject
{
public static IBusinessObject New(BusinessFactory factory)
{ ... }
...
}
这里需要对具体业务工厂的引用来调用BusinessObject.New()
初始值设定项。但唯一有必要参考的是商业工厂本身
我们得到了我们想要的:唯一能够创建
BusinessObject
的人是BusinessFactory
,我认为没有比问题更糟糕的解决方案了,所有这些都需要一个公共静态工厂,这是一个更糟糕的问题,不会阻止人们打电话给工厂来使用你的对象——它不会隐藏任何东西。最好公开一个接口和/或尽可能将构造函数保持为内部,这是最好的保护,因为程序集是受信任的代码
一种选择是使用一个静态构造函数,该构造函数在某处使用类似于IOC容器的东西注册工厂 此解决方案基于在构造函数中使用令牌的思想。在这个回答中完成
这里有另一个解决方案,即“仅仅因为你可以,并不意味着你应该” 它确实满足了保持业务对象构造函数私有并将工厂逻辑放在另一个类中的要求。在那之后,它变得有点粗略 factory类有一个用于创建业务对象的静态方法。它派生自业务对象类,以便访问调用私有构造函数的静态受保护构造方法
public class Person
{
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
Validate();
}
}
工厂是抽象的,所以你不能
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!
}
== 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);
}
}
public interface FactoryObject
{
FactoryObject Instantiate();
}
public class YourClass : FactoryObject
{
static YourClass()
{
Factory.RegisterType(new YourClass());
}
private YourClass() {}
FactoryObject FactoryObject.Instantiate()
{
return new YourClass();
}
}
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();
}
}
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);
}
}
}
}