gpt4 book ai didi

c# - 只允许工厂方法实例化对象(防止实例化基类和未初始化的对象)

转载 作者:太空狗 更新时间:2023-10-29 17:58:38 26 4
gpt4 key购买 nike

我有一个处理“工作”的基类。工厂方法根据作业类型创建派生的“作业处理程序”对象,并确保使用所有作业信息初始化作业处理程序对象。

调用工厂方法为分配的工作和人员请求处理程序:

public enum Job { Clean, Cook, CookChicken }; // List of jobs.

static void Main(string[] args)
{
HandlerBase handler;
handler = HandlerBase.CreateJobHandler(Job.Cook, "Bob");
handler.DoJob();
handler = HandlerBase.CreateJobHandler(Job.Clean, "Alice");
handler.DoJob();
handler = HandlerBase.CreateJobHandler(Job.CookChicken, "Sue");
handler.DoJob();
}

结果:

Bob is cooking.
Alice is cleaning.
Sue is cooking.
Sue is cooking chicken.

作业处理程序类:

public class CleanHandler : HandlerBase
{
protected CleanHandler(HandlerBase handler) : base(handler) { }
public override void DoJob()
{
Console.WriteLine("{0} is cleaning.", Person);
}
}

public class CookHandler : HandlerBase
{
protected CookHandler(HandlerBase handler) : base(handler) { }
public override void DoJob()
{
Console.WriteLine("{0} is cooking.", Person);
}
}

子类作业处理程序:

public class CookChickenHandler : CookHandler
{
protected CookChickenHandler(HandlerBase handler) : base(handler) { }
public override void DoJob()
{
base.DoJob();
Console.WriteLine("{0} is cooking chicken.", Person);
}
}

最好的做事方式?我一直在努力解决这些问题:

  1. 确保所有派生对象都有一个完全初始化的基础对象(分配给个人)。
  2. 防止通过执行所有初始化的工厂方法以外的任何对象实例化。
  3. 防止基类对象的实例化。

作业处理程序 HandlerBase基类:

  1. A Dictionary<Job,Type>将作业映射到处理程序类。
  2. 工作数据(即 Person)的 PRIVATE setter 防止通过工厂方法访问。
  3. 没有默认构造函数和 PRIVATE 构造函数阻止构造,除非通过工厂方法。
  4. protected “复制构造函数”是唯一的非私有(private)构造函数。必须有一个实例化的 HandlerBase 才能创建一个新对象,只有基类工厂才能创建一个基类 HandlerBase 对象。如果试图从非基础对象创建新对象,“复制构造函数”会抛出异常(同样,除了工厂方法之外,阻止构造)。

看一下基类:

public class HandlerBase
{
// Dictionary maps Job to proper HandlerBase type.
private static Dictionary<Job, Type> registeredHandlers =
new Dictionary<Job, Type>() {
{ Job.Clean, typeof(CleanHandler) },
{ Job.Cook, typeof(CookHandler) },
{ Job.CookChicken, typeof(CookChickenHandler) }
};

// Person assigned to job. PRIVATE setter only accessible to factory method.
public string Person { get; private set; }

// PRIVATE constructor for data initialization only accessible to factory method.
private HandlerBase(string name) { this.Person = name; }

// Non-private "copy constructor" REQUIRES an initialized base object.
// Only the factory method can make a HandlerBase object.
protected HandlerBase(HandlerBase handler)
{
// Prevent creating new objects from non-base objects.
if (handler.GetType() != typeof(HandlerBase))
throw new ArgumentException("THAT'S ILLEGAL, PAL!");

this.Person = handler.Person; // peform "copy"
}

// FACTORY METHOD.
public static HandlerBase CreateJobHandler(Job job, string name)
{
// Look up job handler in dictionary.
Type handlerType = registeredHandlers[job];

// Create "seed" base object to enable calling derived constructor.
HandlerBase seed = new HandlerBase(name);

object[] args = new object[] { seed };
BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic;

HandlerBase newInstance = (HandlerBase)Activator
.CreateInstance(handlerType, flags, null, args, null);

return newInstance;
}

public virtual void DoJob() { throw new NotImplementedException(); }
}

工厂方法一览:

因为我已经在没有实例化基础对象的情况下不可能公开构造新对象,所以工厂方法首先构造一个 HandlerBase。实例作为调用所需派生类“复制构造函数”的“种子”。

工厂方法使用Activator.CreateInstance()实例化新对象。 Activator.CreateInstance() 查找与请求的签名匹配的构造函数:

所需的构造函数是 DerivedHandler(HandlerBase handler) ,因此,

  1. 我的“种子”HandlerBase对象放置在 object[] args .
  2. 我合并BindingFlags.InstanceBindingFlags.NonPublic以便 CreateInstance() 搜索非公共(public)构造函数(添加 BindingFlags.Public 以查找公共(public)构造函数)。
  3. Activator.CreateInstance() 实例化返回的新对象。

我不喜欢什么...

在实现接口(interface)或类时不强制执行构造函数。派生类中的构造函数代码是强制性的:

protected DerivedJobHandler(HandlerBase handler) : base(handler) { }

但是,如果省略了构造函数,您不会收到友好的编译器错误,告诉您所需的确切方法签名:“'DerivedJobHandler' 不包含采用 0 个参数的构造函数”。

也可以编写一个构造函数来消除任何编译器错误,而不是——更糟糕!——导致运行时错误:

protected DerivedJobHandler() : base(null) { }

我不喜欢在派生类实现中无法强制执行必需的构造函数。

最佳答案

我在您的 HandlerBase 中看到了三个职责,如果它们相互分离,可能会简化设计问题。

  1. 处理程序的注册
  2. 处理程序的构造
  3. 工作

重组的一种方法是将#1 和#2 放在工厂类上,将#3 放在具有内部构造函数的类上,这样只有工厂类可以根据您的内部要求调用它。您可以直接传入 Person 和 Job 值,而不是让构造函数从不同的 HandlerBase 实例中提取它们,这样可以使代码更易于理解。

一旦这些职责被分离,您就可以更轻松地独立发展它们。

关于c# - 只允许工厂方法实例化对象(防止实例化基类和未初始化的对象),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6560902/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com