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

2023-12-27

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

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

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. 确保所有派生对象都有一个完全初始化的基础对象(指定的 Person)。
  2. 除了通过执行所有初始化的工厂方法之外,防止任何对象的实例化。
  3. 防止基类对象的实例化。

作业处理程序HandlerBase基类:

  1. A Dictionary<Job,Type>将作业映射到处理程序类。
  2. 作业数据(即人员)的 PRIVATE setter 阻止访问,除非通过工厂方法。
  3. 除了工厂方法之外,没有默认构造函数和 PRIVATE 构造函数会阻止构造。
  4. 受保护的“复制构造函数”是唯一的非私有构造函数。必须有一个实例化的 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), thus,

  1. 我的“种子”HandlerBase物体被放置在object[] args.
  2. 我结合BindingFlags.Instance and BindingFlags.NonPublic以便 CreateInstance() 搜索非公共构造函数(添加BindingFlags.Public找到一个公共构造函数)。
  3. Activator.CreateInstance() 实例化返回的新对象。

我不喜欢什么...

实现接口或类时不强制使用构造函数。派生类中的构造函数代码是强制性的:

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

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

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

protected DerivedJobHandler() : base(null) { }

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


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

  1. 处理程序登记
  2. 处理程序的构建
  3. DoJob

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

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

只允许工厂方法实例化对象(防止实例化基类和未初始化的对象) 的相关文章

  • 为 Visual Studio 2013 编译 Tesseract

    我正在尝试使用tesseract在 Visual Studio 2013 中 我在链接器 gt 输入 不是 libtesseract302 static lib 中使用 libtesseract302 lib 一切都正常 并且已编译并运行
  • 单元测试一起运行时失败,单独运行时通过

    所以我的单元测试遇到了一些问题 我不能只是将它们复制并粘贴到这里 但我会尽力而为 问题似乎是 如果我一项一项地运行测试 一切都会按预期进行 但如果我告诉它一起运行测试 则 1 5 将通过 TestMethod public void Obj
  • C++中的类查找结构体数组

    我正在尝试创建一个结构数组 它将输入字符串链接到类 如下所示 struct string command CommandPath cPath cPathLookup set an alarm AlarmCommandPath send an
  • 存储来自其他程序的事件

    我想将其他应用程序的事件存储在我自己的应用程序中 事件示例 打开 最小化 Word 或打开文件时 这样的事可能吗 运行程序 http msdn microsoft com en us library ms813609 aspx and 打开
  • 回发或回调参数无效。使用“”启用事件验证

    当我从客户端回发页面时 出现以下错误 我有修改客户端 asp ListBox 的 JavaScript 代码 我们该如何解决这个问题 错误详细信息如下 Server Error in XXX Application Invalid post
  • 关于在 Windows 上使用 WiFi Direct Api?

    我目前正在开发一个应用程序 我需要在其中创建链接 阅读 无线网络连接 在桌面应用程序 在 Windows 10 上 和平板电脑 Android 但无关紧要 之间 工作流程 按钮 gt 如果需要提升权限 gt 创建类似托管网络的 WiFi 网
  • 如何在 Linq 中获得左外连接?

    我的数据库中有两个表 如下所示 顾客 C ID city 1 Dhaka 2 New york 3 London 个人信息 P ID C ID Field value 1 1 First Name Nasir 2 1 Last Name U
  • 将 Excel 导入到 Datagridview

    我使用此代码打开 Excel 文件并将其保存在 DataGridView 中 string name Items string constr Provider Microsoft Jet OLEDB 4 0 Data Source Dial
  • C++:.bmp 到文件中的字节数组

    是的 我已经解决了与此相关的其他问题 但我发现它们没有太大帮助 他们提供了一些帮助 但我仍然有点困惑 所以这是我需要做的 我们有一个 132x65 的屏幕 我有一个 132x65 的 bmp 我想遍历 bmp 并将其分成小的 1x8 列以获
  • 批量更新 SQL Server C#

    我有一个 270k 行的数据库 带有主键mid和一个名为value 我有一个包含中值和值的文本文件 现在我想更新表格 以便将每个值分配给正确的中间值 我当前的方法是从 C 读取文本文件 并为我读取的每一行更新表中的一行 必须有更快的方法来做
  • 上下文敏感与歧义

    我对上下文敏感性和歧义如何相互影响感到困惑 我认为正确的是 歧义 歧义语法会导致使用左推导或右推导构建多个解析树 所有可能的语法都是二义性的语言是二义性语言 例如 C 是一种不明确的语言 因为 x y 总是可以表示两个不同的事物 如下所述
  • 如何在 Blackberry Cascades 中显示具有特定号码的电话板

    我正在使用带有 C QT 和 QML 的 Blackberry Cascades 10 Beta 3 SDK 以及 Blackberry 10 Dev Alpha Simulator 和 QNX Momentics IDE 并且我正在尝试实
  • 使用 Moq 使用内部构造函数模拟类型

    我正在尝试模拟 Microsoft Sync Framework 中的一个类 它只有一个内部构造函数 当我尝试以下操作时 var fullEnumerationContextMock new Mock
  • .NET中的LinkedList是循环链表吗?

    我需要一个循环链表 所以我想知道是否LinkedList是循环链表吗 每当您想要移动列表中的 下一个 块时 以循环方式使用它的快速解决方案 current current Next current List First 电流在哪里Linke
  • (de)从 CSV 序列化为对象(或者最好是类型对象的列表)

    我是一名 C 程序员 试图学习 C 似乎有一些内置的对象序列化 但我在这里有点不知所措 我被要求将测试数据从 CSV 文件加载到对象集合中 CSV 比 xml 更受青睐 因为它更简单且更易于人类阅读 我们正在创建测试数据来运行单元测试 该集
  • gcc 的配置选项如何确定默认枚举大小(短或非短)?

    我尝试了一些 gcc 编译器来查看默认枚举大小是否很短 至少一个字节 强制使用 fshort enums 或无短 至少 4 个字节 强制使用 fno short enums user host echo Static assert 4 si
  • 有没有办法强制显示工具提示?

    我有一个验证字段的方法 如果无法验证 该字段将被清除并标记为红色 我还希望在框上方弹出一个工具提示 并向用户显示该值无效的消息 有没有办法做到这一点 并且可以控制工具提示显示的时间 我怎样才能让它自己弹出而不是鼠标悬停时弹出 If the
  • 线程和 fork()。我该如何处理呢? [复制]

    这个问题在这里已经有答案了 可能的重复 多线程程序中的fork https stackoverflow com questions 1235516 fork in multi threaded program 如果我有一个使用 fork 的
  • 使用 GROUP 和 SUM 的 LINQ 查询

    请帮助我了解如何使用带有 GROUP 和 SUM 的 LINQ 进行查询 Query the database IEnumerable
  • 如何使用 Word Automation 获取页面范围

    如何使用办公自动化找到 Microsoft Word 中第 n 页的范围 似乎没有 getPageRange n 函数 并且不清楚它们是如何划分的 这就是您从 VBA 执行此操作的方法 转换为 Matlab COM 调用应该相当简单 Pub

随机推荐

  • SpriteKit SKScene 缺少触摸已结束

    我注意到 touchesEnded 并不总是在多点触摸时传递到 SKScene 根据移开手指等的速度 我会永久错过一些已结束的触摸 TouchsCancelled 已实现 我添加了一个自定义 UIView 并将其放在屏幕的左侧 没有问题 我
  • google firebase函数教程意外令牌=>

    我已经进行了一些谷歌搜索 但没有找到我的问题的答案 我正在关注 google firebase 函数的教程here https firebase google com docs functions get started authuser
  • 将绘图图保存到 html 文件后,您可以稍后将其重新读取为图吗?

    我想编辑数据 可能会向图表添加更多痕迹 我找到了一种将 html 文件显示为图表的方法 但不对其进行编辑 from IPython display import HTML HTML filename file name html 通常情况下
  • 为什么 getPasswordAuthentication() 没有被调用?

    import java io BufferedReader import java io IOException import java io InputStream import java io InputStreamReader imp
  • Strongloop:用[and]和[or]条件一起过滤数据

    我正在尝试使用 and 和 or 条件过滤数据 我想得到这个 mySql 查询 SELECT FROM data WHERE property1 11 OR property1 13 AND property2 6 我写的其余 api 是这
  • 更改 Jupyter Notebook 中的主题?

    我喜欢黑暗主题 但是 Jupyter笔记本的默认主题是浅色 我找不到更改主题 背景颜色的选项 这是怎么做到的 使用以下命令很容易做到这一点jupyter themes凯尔 杜诺万 Kyle Dunovan 的包装 您也许可以使用以下命令安装
  • 从 SCM 运行声明性 JenkinsFile 时出现“java.lang.NoSuchMethodError:找不到此类 DSL 方法‘管道’”

    我们有一个以声明式风格编写的非常简单的 Jenkinsfile pipeline agent any stages stage Test steps echo hello 当我们直接从 Jenkins v 2 107 0 中的管道作业中运行
  • Perl:正确传递数组供线程处理

    我正在学习如何在 Perl 中进行线程处理 我正在查看示例代码here https stackoverflow com questions 11596493 how to limit the max number of parallel t
  • 带 RecyclerView 的 AlertDialog

    我想创建自定义对话框 其中包含我的应用程序中的某些项目的列表 这是我的适配器代码 上下文上下文 ArrayList 状态列表 public MaritalStatusAdapter Context context ArrayList
  • NUnit 示例代码?

    我想学习如何使用 NUnit 我通过阅读然后使用真实代码来学习最好的方法 在哪里可以找到以示例方式使用 NUnit 的小型 简单的 C 项目 上面有很多很好的例子NUnit 的开发者维基 http nunit com devwiki cgi
  • 使用 scala、junit 时 Powermock 无法模拟静态方法

    我之前曾将 Powermock 与 java 和 junit 一起使用 我已经成功地能够模拟静态方法 如下例所示 PrepareForTest TimeHelper class MainApp class RunWith PowerMock
  • C# 中 get、set 属性的真正目的是什么? [复制]

    这个问题在这里已经有答案了 可能的重复 属性与方法 https stackoverflow com questions 601621 properties vs methods C 公共字段与自动属性 https stackoverflow
  • 为多个域设置 httpS

    我需要为多个域名 xxxx com xxxx net 设置 https 使用单个通用证书 我们购买证书的 CA 要求创建证书签名请求 CSR 但是当我使用 openssl 生成它时 它只要求一个名称 如何为多个域制定一个 CSR 避免使用多
  • Angular2 中的条件验证

    我想对访客和客户注册表使用相同的模板 但验证可能会有所不同 如下所示 让我们假设它是宾客登记表 需要名字
  • Rails 3.0 设计通过回答问题重置密码

    我正在使用带有 Rails 3 的设备 在用户模型中 我为问题和该问题的答案创建了字段 我想知道如何按以下逻辑实现忘记密码 在忘记密码页面上用户输入用户名 在下一步中 应用程序应显示存储在数据库中的问题 用户回答问题 如果答案与数据库中存储
  • 随机访问随机排列[关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我想生成一个非常大的伪随机排列 p
  • SQL 查询查找特定 id 具有不同值但应相同的产品

    我需要一个 SQL 查询 它将获取相同 id 具有不同值的产品列表 产品属于表 A 和 Id 值属于表 B 并且两个表都可以通过列名 prod id 连接 我想要的输出 项目清单 product abc 因此 在输出中 我只需要产品 abc
  • 代码完成不列出消息处理程序

    When working on an old project in Delphi XE2 the code completion window that pops up after CTRL SPACE does not list mess
  • 按键从数组中删除行[重复]

    这个问题在这里已经有答案了 可能的重复 PHP 如何从数组中删除特定元素 https stackoverflow com questions 2448964 php how to remove specific element from a
  • 只允许工厂方法实例化对象(防止实例化基类和未初始化的对象)

    我有一个处理 工作 的基类 工厂方法根据作业类型创建派生的 作业处理程序 对象 并确保使用所有作业信息初始化作业处理程序对象 调用工厂方法来请求分配的作业和人员的处理程序 public enum Job Clean Cook CookChi