GoF Factory 的命名约定?

2023-12-15

该模式使用抽象工厂,然后使用工厂的实现。

我确信这两个类有一个标准的命名约定,但我不知道它是什么。

例如:

public abstract class ChocolateFactory { };

public class MyChocolateFactory { } : ChocolateFactory

这里的标准约定是什么?

我正在考虑 ChocolateFactoryBase 或 ConcreteChocolateFactory,但也许还有其他东西(很像 Enums 往往以 Enum 为后缀,例如PetTypeEnum这样你就可以做PetTypeEnum PetType;

希望这不是主观的。


问题与答案

OK,这道题就从一个抽象工厂的命名问题开始。根据经验,始终使用您正在做的事情的“正式”名称(例如 Factory、Decorator 等)和具体实现的描述(例如 Snickers、Mars、MotifWidget 等)。

所以基本上,你创建一个MSSQLConnection这是你所描述的具体事物,并且Factory这意味着它遵循工厂模式的特点。

好的,到目前为止命名和最初的问题。现在来说说很酷的东西。讨论的是在 C# 中实现抽象工厂的最佳方式,这是一个不同的主题。我在 C# 中实现所有设计模式方面做了相当多的工作,为此我将在这里分享一些有关工厂的详细信息。开始:

抽象工厂和工厂

抽象工厂基本上是基类或接口与具体实现的组合。如果您共享大量代码,则需要基类;如果不需要,则需要接口。

我通常区分“工厂”和“抽象工厂”。工厂是创建(某种类型)对象的事物,“抽象工厂”是创建任意类型的对象的事物。因此,可以得出以下结论:抽象工厂 is a factory。这与下一条信息相关。

工厂模式

支持RTTI的语言都能够实现工厂模式。工厂模式是创建对象的东西。最简单的实现是一个只包含创建对象的方法的类,例如:

// ...

public void CreateConnection()
{
    return new SqlConnection();
}

// ...

你通常用它来抽象事物。例如,HTML解析器中生成XML节点的东西根据HTML标签创建某种类型的节点。

工厂通常根据运行时信息做出决策。因此,可以推广工厂模式来实现如下内容:

public T Create(string name) 
{
    // lookup constructor, invoke.
}

使用 RTTI 创建通用工厂模式非常容易,该模式存储Type对于每个名字。查找名称,使用反射创建对象。完毕。

哦,作为奖励,你需要编写的代码比手工制作所有工厂要少得多。因为所有实现都是相同的,所以您不妨将其放在基类中并在静态构造函数中填充字典。

概括抽象工厂

抽象工厂基本上是工厂的集合,它们以与工厂模式相同的方式创建对象。唯一共享的是接口(例如创建,或者您可以使用继承来创建抽象)。

实现非常简单,所以我就到此为止。

解耦工厂和类型

让我们回到 GoF 的例子。他们谈论一个MotifFactory and a PMFactory。将来我们会遇到另一个 UI 问题,我们需要一个ASPNETFactory or a SilverlightFactory。然而,未来是未知的,如果不需要,我们宁愿不发布旧的 DLL - 毕竟,这不灵活。

如果我们想向工厂添加新方法,就会出现第二个问题。因此,这样做将涉及改变所有工厂。正如您可能已经猜到的,我不想在多个地方更改此设置。

幸运的是,我们可以解决这两个问题。接口是相同的(甚至可以通用),因此我们可以在运行时简单地向工厂添加新功能。

我们可以使用属性来告诉类它应该被具体化,而不是告诉工厂要创建什么对象by某工厂。我们还可以在加载程序集期间扫描所有类型,因此如果加载程序集,我们可以简单地即时构建新工厂。

我为此牺牲的是编译时检查,但由于工厂模式通常使用运行时信息,因此这不一定是问题。

总结一下,这是我的工厂的代码:

/// <summary>
/// This attribute is used to tag classes, enabling them to be constructed by a Factory class. See the <see cref="Factory{Key,Intf}"/> 
/// class for details.
/// </summary>
/// <remarks>
/// <para>
/// It is okay to mark classes with multiple FactoryClass attributes, even when using different keys or different factories.
/// </para>
/// </remarks>
/// <seealso cref="Factory{Key,Intf}"/>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
public class FactoryClassAttribute : Attribute
{
    /// <summary>
    /// This marks a class as eligible for construction by the specified factory type.
    /// </summary>
    /// <example>
    /// [FactoryClass("ScrollBar", typeof(MotifFactory))]
    /// public class MotifScrollBar : IControl { }
    /// </example>
    /// <param name="key">The key used to construct the object</param>
    /// <param name="factoryType">The type of the factory class</param>
    public FactoryClassAttribute(object key, Type factoryType)
    {
        if ((factoryType.IsGenericType &&
             factoryType.GetGenericTypeDefinition() == typeof(Factory<,>)) ||
            factoryType.IsAbstract || 
            factoryType.IsInterface)
        {
            throw new NotSupportedException("Incorrect factory type: you cannot use GenericFactory or an abstract type as factory.");
        }
        this.Key = key;
        this.FactoryType = factoryType;
    }

    /// <summary>
    /// The key used to construct the object when calling the <see cref="Factory{Key,Intf}.Create(Key)"/> method.
    /// </summary>
    public object Key { get; private set; }

    /// <summary>
    /// The type of the factory class
    /// </summary>
    public Type FactoryType { get; private set; }
}

/// <summary>
/// Provides an interface for creating related or dependent objects.
/// </summary>
/// <remarks>
/// <para>
/// This class is an implementation of the Factory pattern. Your factory class should inherit this Factory class and 
/// you should use the [<see cref="FactoryClassAttribute"/>] attribute on the objects that are created by the factory.
/// The implementation assumes all created objects share the same constructor signature (which is not checked by the Factory). 
/// All implementations also share the same <typeparamref name="Intf"/> type and are stored by key. During runtime, you can 
/// use the Factory class implementation to build objects of the correct type.
/// </para>
/// <para>
/// The Abstract Factory pattern can be implemented by adding a base Factory class with multiple factory classes that inherit from 
/// the base class and are used for registration. (See below for a complete code example).
/// </para>
/// <para>
/// Implementation of the Strategy pattern can be done by using the Factory pattern and making the <typeparamref name="Intf"/>
/// implementations algorithms. When using the Strategy pattern, you still need to have some logic that picks when to use which key.
/// In some cases it can be useful to use the Factory overload with the type conversion to map keys on other keys. When implementing 
/// the strategy pattern, it is possible to use this overload to determine which algorithm to use.
/// </para>
/// </remarks>
/// <typeparam name="Key">The type of the key to use for looking up the correct object type</typeparam>
/// <typeparam name="Intf">The base interface that all classes created by the Factory share</typeparam>
/// <remarks>
/// The factory class automatically hooks to all loaded assemblies by the current AppDomain. All classes tagged with the FactoryClass
/// are automatically registered.
/// </remarks>
/// <example>
/// <code lang="c#">
/// // Create the scrollbar and register it to the factory of the Motif system
/// [FactoryClass("ScrollBar", typeof(MotifFactory))]
/// public class MotifScrollBar : IControl { }
/// 
/// // [...] add other classes tagged with the FactoryClass attribute here...
///
/// public abstract class WidgetFactory : Factory&lt;string, IControl&gt;
/// {
///     public IControl CreateScrollBar() { return Create("ScrollBar") as IScrollBar; }
/// }
///
/// public class MotifFactory : WidgetFactory { }
/// public class PMFactory : WidgetFactory { }
///
/// // [...] use the factory to create a scrollbar
/// 
/// WidgetFactory widgetFactory = new MotifFactory();
/// var scrollbar = widgetFactory.CreateScrollBar(); // this is a MotifScrollbar intance
/// </code>
/// </example>
public abstract class Factory<Key, Intf> : IFactory<Key, Intf>
    where Intf : class
{
    /// <summary>
    /// Creates a factory by mapping the keys of the create method to the keys in the FactoryClass attributes.
    /// </summary>
    protected Factory() : this((a) => (a)) { }

    /// <summary>
    /// Creates a factory by using a custom mapping function that defines the mapping of keys from the Create 
    /// method, to the keys in the FactoryClass attributes.
    /// </summary>
    /// <param name="typeConversion">A function that maps keys passed to <see cref="Create(Key)"/> to keys used with [<see cref="FactoryClassAttribute"/>]</param>
    protected Factory(Func<Key, object> typeConversion)
    {
        this.typeConversion = typeConversion;
    }

    private Func<Key, object> typeConversion;
    private static object lockObject = new object();
    private static Dictionary<Type, Dictionary<object, Type>> dict = null;

    /// <summary>
    /// Creates an instance a class registered with the <see cref="FactoryClassAttribute"/> attribute by looking up the key.
    /// </summary>
    /// <param name="key">The key used to lookup the attribute. The key is first converted using the typeConversion function passed 
    /// to the constructor if this was defined.</param>
    /// <returns>An instance of the factory class</returns>
    public virtual Intf Create(Key key)
    {
        Dictionary<Type, Dictionary<object, Type>> dict = Init();
        Dictionary<object, Type> factoryDict;
        if (dict.TryGetValue(this.GetType(), out factoryDict))
        {
            Type t;
            return (factoryDict.TryGetValue(typeConversion(key), out t)) ? (Intf)Activator.CreateInstance(t) : null;
        }
        return null;
    }

    /// <summary>
    /// Creates an instance a class registered with the <see cref="FactoryClassAttribute"/> attribute by looking up the key.
    /// </summary>
    /// <param name="key">The key used to lookup the attribute. The key is first converted using the typeConversion function passed 
    /// to the constructor if this was defined.</param>
    /// <param name="constructorParameters">Additional parameters that have to be passed to the constructor</param>
    /// <returns>An instance of the factory class</returns>
    public virtual Intf Create(Key key, params object[] constructorParameters)
    {
        Dictionary<Type, Dictionary<object, Type>> dict = Init();
        Dictionary<object, Type> factoryDict;
        if (dict.TryGetValue(this.GetType(), out factoryDict))
        {
            Type t;
            return (factoryDict.TryGetValue(typeConversion(key), out t)) ? (Intf)Activator.CreateInstance(t, constructorParameters) : null;
        }
        return null;
    }

    /// <summary>
    /// Enumerates all registered attribute keys. No transformation is done here.
    /// </summary>
    /// <returns>All keys currently known to this factory</returns>
    public virtual IEnumerable<Key> EnumerateKeys()
    {
        Dictionary<Type, Dictionary<object, Type>> dict = Init();
        Dictionary<object, Type> factoryDict;
        if (dict.TryGetValue(this.GetType(), out factoryDict))
        {
            foreach (object key in factoryDict.Keys)
            {
                yield return (Key)key;
            }
        }
    }

    private void TryHook()
    {
        AppDomain.CurrentDomain.AssemblyLoad += new AssemblyLoadEventHandler(NewAssemblyLoaded);
    }

    private Dictionary<Type, Dictionary<object, Type>> Init()
    {
        Dictionary<Type, Dictionary<object, Type>> d = dict;
        if (d == null)
        {
            lock (lockObject)
            {
                if (dict == null)
                {
                    try
                    {
                        TryHook();
                    }
                    catch (Exception) { } // Not available in this security mode. You're probably using shared hosting

                    ScanTypes();
                }
                d = dict;
            }
        }
        return d;
    }

    private void ScanTypes()
    {
        Dictionary<Type, Dictionary<object, Type>> classDict = new Dictionary<Type, Dictionary<object, Type>>();
        foreach (Assembly ass in AppDomain.CurrentDomain.GetAssemblies())
        {
            AddAssemblyTypes(classDict, ass);
        }
        dict = classDict;
    }

    private void AddAssemblyTypes(Dictionary<Type, Dictionary<object, Type>> classDict, Assembly ass)
    {
        try
        {
            foreach (Type t in ass.GetTypes())
            {
                if (t.IsClass && !t.IsAbstract &&
                    typeof(Intf).IsAssignableFrom(t))
                {
                    object[] fca = t.GetCustomAttributes(typeof(FactoryClassAttribute), false);
                    foreach (FactoryClassAttribute f in fca)
                    {
                        if (!(f.Key is Key))
                        {
                            throw new InvalidCastException(string.Format("Cannot cast key of factory object {0} to {1}", t.FullName, typeof(Key).FullName));
                        }
                        Dictionary<object, Type> keyDict;
                        if (!classDict.TryGetValue(f.FactoryType, out keyDict))
                        {
                            keyDict = new Dictionary<object, Type>();
                            classDict.Add(f.FactoryType, keyDict);
                        }
                        keyDict.Add(f.Key, t);
                    }
                }
            }
        }
        catch (ReflectionTypeLoadException) { } // An assembly we cannot process. That also means we cannot use it.
    }

    private void NewAssemblyLoaded(object sender, AssemblyLoadEventArgs args)
    {
        lock (lockObject)
        {
            // Make sure new 'create' invokes wait till we're done updating the factory
            Dictionary<Type, Dictionary<object, Type>> classDict = new Dictionary<Type, Dictionary<object, Type>>(dict);
            dict = null;
            Thread.MemoryBarrier();

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

GoF Factory 的命名约定? 的相关文章

  • 检查两个数是否是彼此的排列?

    给定两个数字 a b 使得 1 例如 123 是 312 的有效排列 我也不想对数字中的数字进行排序 如果您指的是数字的字符 例如 1927 和 9721 则 至少 有几种方法 如果允许排序 一种方法是简单地sprintf将它们放入两个缓冲
  • 是否可以强制 XMLWriter 将元素写入单引号中?

    这是我的代码 var ptFirstName tboxFirstName Text writer WriteAttributeString first ptFirstName 请注意 即使我使用 ptFirstName 也会以双引号结束 p
  • ASP.NET Core Serilog 未将属性推送到其自定义列

    我有这个设置appsettings json对于我的 Serilog 安装 Serilog MinimumLevel Information Enrich LogUserName Override Microsoft Critical Wr
  • pthread_cond_timedwait() 和 pthread_cond_broadcast() 解释

    因此 我在堆栈溢出和其他资源上进行了大量搜索 但我无法理解有关上述函数的一些内容 具体来说 1 当pthread cond timedwait 因为定时器值用完而返回时 它如何自动重新获取互斥锁 互斥锁可能被锁定在其他地方 例如 在生产者
  • 获取没有非标准端口的原始 url (C#)

    第一个问题 环境 MVC C AppHarbor Problem 我正在调用 openid 提供商 并根据域生成绝对回调 url 在我的本地机器上 如果我点击的话 效果很好http localhost 12345 login Request
  • 如何将图像和 POST 数据上传到 Azure 移动服务 ApiController 终结点?

    我正在尝试上传图片and POST表单数据 尽管理想情况下我希望它是json 到我的端点Azure 移动服务应用 我有ApiController method HttpPost Route api upload databaseId sea
  • 将目录压缩为单个文件的方法有哪些

    不知道怎么问 所以我会解释一下情况 我需要存储一些压缩文件 最初的想法是创建一个文件夹并存储所需数量的压缩文件 并创建一个文件来保存有关每个压缩文件的数据 但是 我不被允许创建许多文件 只能有一个 我决定创建一个压缩文件 其中包含有关进一步
  • WPF TabControl,用C#代码更改TabItem的背景颜色

    嗨 我认为这是一个初学者的问题 我搜索了所有相关问题 但所有这些都由 xaml 回答 但是 我需要的是后台代码 我有一个 TabControl 我需要设置其项目的背景颜色 我需要在选择 取消选择和悬停时为项目设置不同的颜色 非常感谢你的帮助
  • Web API - 访问 DbContext 类中的 HttpContext

    在我的 C Web API 应用程序中 我添加了CreatedDate and CreatedBy所有表中的列 现在 每当在任何表中添加新记录时 我想填充这些列 为此目的我已经覆盖SaveChanges and SaveChangesAsy
  • 如何将图像路径保存到Live Tile的WP8本地文件夹

    我正在更新我的 Windows Phone 应用程序以使用新的 WP8 文件存储 API 本地文件夹 而不是 WP7 API 隔离存储文件 旧的工作方法 这是我如何成功地将图像保存到 共享 ShellContent文件夹使用隔离存储文件方法
  • 从路径中获取文件夹名称

    我有一些路c server folderName1 another name something another folder 我如何从那里提取最后一个文件夹名称 我尝试了几件事 但没有成功 我只是不想寻找最后的 然后就去休息了 Thank
  • Github Action 在运行可执行文件时卡住

    我正在尝试设置运行google tests on a C repository using Github Actions正在运行的Windows Latest 构建过程完成 但是当运行测试时 它被卡住并且不执行从生成的可执行文件Visual
  • 从库中捕获主线程 SynchronizationContext 或 Dispatcher

    我有一个 C 库 希望能够将工作发送 发布到 主 ui 线程 如果存在 该库可供以下人员使用 一个winforms应用程序 本机应用程序 带 UI 控制台应用程序 没有 UI 在库中 我想在初始化期间捕获一些东西 Synchronizati
  • 将 xml 反序列化为类,list<> 出现问题

    我有以下 XML
  • 将文本叠加在图像背景上并转换为 PDF

    使用 NET 我想以编程方式创建一个 PDF 它仅包含一个背景图像 其上有两个具有不同字体和位置的标签 我已阅读过有关现有 PDF 库的信息 但不知道 如果适用 哪一个对于如此简单的任务来说最简单 有人愿意指导我吗 P D 我不想使用生成的
  • WCF:将随机数添加到 UsernameToken

    我正在尝试连接到用 Java 编写的 Web 服务 但有些东西我无法弄清楚 使用 WCF 和 customBinding 几乎一切似乎都很好 除了 SOAP 消息的一部分 因为它缺少 Nonce 和 Created 部分节点 显然我错过了一
  • 为什么我收到“找不到编译动态表达式所需的一种或多种类型。”?

    我有一个已更新的项目 NET 3 5 MVC v2 到 NET 4 0 MVC v3 当我尝试使用或设置时编译出现错误 ViewBag Title财产 找不到编译动态表达式所需的一种或多种类型 您是否缺少对 Microsoft CSharp
  • Validation.ErrorTemplate 的 Wpf 动态资源查找

    在我的 App xaml 中 我定义了一个资源Validation ErrorTemplate 这取决于动态BorderBrush资源 我打算定义独特的BorderBrush在我拥有的每个窗口以及窗口内的不同块内
  • 如何在 C++ BOOST 中像图形一样加载 TIFF 图像

    我想要加载一个 tiff 图像 带有带有浮点值的像素的 GEOTIFF 例如 boost C 中的图形 我是 C 的新手 我的目标是使用从源 A 到目标 B 的双向 Dijkstra 来获得更高的性能 Boost GIL load tiif
  • 使用按位运算符相乘

    我想知道如何使用按位运算符将一系列二进制位相乘 但是 我有兴趣这样做来查找二进制值的十进制小数值 这是我正在尝试做的一个例子 假设 1010010 我想使用每个单独的位 以便将其计算为 1 2 1 0 2 2 1 2 3 0 2 4 虽然我

随机推荐

  • css3 逐帧动画

    我需要逐帧制作动画来更改元素的背景位置 我需要在没有缓动或线性动画的情况下执行此操作 只需更改背景位置即可 用css3动画可以实现吗 是的 它是通过新的阶跃函数缓动曲线属性实现的 使用 step start 或 step end 代替 ea
  • Oracle 11g 中的 XML 表

    这是一个示例表 create table xmltemp mydoc xmltype 这是一个小的 xml 文档 insert into xmltemp values xmltype
  • 如何阻止类对象的构造?

    我在任何地方都找不到答案 可以通过使用条件并抛出异常来完成 但是还有其他方法吗 目前尚不清楚为什么您想要一个在任何情况下都无法构造的类 但您可以使所有构造函数private并没有为他们提供任何实施 这将阻止编译时的构造 在 C 11 中你可
  • 如何在 bash shell 中格式化字符串?

    我正在尝试在linux中格式化变量 str Initial Value 168 echo New Value echo str cut d f2 gt gt test txt 我期待以下输出 Value 168 但相反得到 Value 16
  • 子集 data.table 仅保留大于应用于所有列的特定值的元素

    我想要子集news 下 创建news2 下文进一步 将仅包括每个元素中的abs 值 的行 列news gt 0 01 下面是我尝试过的代码 gr lt data frame which abs news 1 ncol news with F
  • mysql_real_escape_string() 不应该在数据库中留下斜杠吗?

    我正在使用 smarty 和mysql real escape string 对于用户输入 当我插入一些代码时 or 并在 phpmyadmin 中查找 它显示没有反斜杠 当我从数据库获取记录时 我也没有反斜杠 但是当我只是传递转义字符串而
  • 仅字母、下划线、连字符、撇号的正则表达式

    我想要一个只接受字母 连字符 撇号 下划线的正则表达式 我试过 A Za z 但它不起作用 请帮忙 你的正则表达式是错误的 尝试这个 0 9A Za z OR w 连字符需要位于字符类中的第一个或最后一个位置以避免转义 另外 如果不允许空字
  • 使用多线程模块将 API 数据检索到数据帧中

    我正在使用第三方 API 从大量天数中检索不同标签的 10 分钟数据 当前数据提取可能需要长达几分钟的时间 具体取决于天数和标签数量 因此 我正在尝试多线程 我知道这对于繁重的 IO 操作很有用 API 调用如下 我已替换了实际的 API
  • 在 d3.js 中嵌入 json 文件

    http bl ocks org mbostock 4339083我正在使用这个 代替d3 json d 4063550 flare json function error flare 我如何让它使用 html 中的 json 文件 就像我
  • 如何将 ZipInputStream 转换为 InputStream?

    我有代码 其中 ZipInputSream 转换为 byte 但我不知道如何将其转换为输入流 private void convertStream String encoding ZipInputStream in throws IOExc
  • 如何在 Swift 中的两个场景之间传递数据?

    我有两个场景 我想使用转场将单个变量传递到另一个场景 我已经尝试过 但不幸的是我见过的所有教程都涉及故事板 我没有使用故事板 我正在以编程方式完成所有这些工作 这是我试图初始化的segue func prepareForSegue segu
  • 刷新 recyclerview 片段中的项目

    我的应用程序有 4 个带有滑动选项卡布局的选项卡 我在 MainActivity 中使用了一个视图寻呼机 每个选项卡都有 4 个片段 我使用 volley 从 mysql 接收数据 卡片视图 回收者视图和 JSON 我想使用 SwipeRe
  • 与 MATLAB SQL Server 进行通信

    有没有办法有效地沟通这些平台 我是说 像导出 SQL Server 和加载 Matlab 之类的东西 但以有效的方式 我找到了adodb 工具到目前为止 文件交换工作得最好 它比数据库工具箱或任何先前的包装解决方案要快得多 不需要额外的工具
  • 在 OpenCV 中使用 FeatureDetector 会导致访问冲突

    我需要找到并匹配立体图像中的特征点 因此我想比较 OpenCV 2 4 5 支持的不同特征检测算法 通过将 SURF SIFT 等传递给函数 代码片段 include opencv2 opencv hpp include
  • 绘制鼠标指针图标?

    我正在编写一些有趣的小工具 我希望能够在与原始鼠标不同的位置绘制第二个 或更多 鼠标指针图标 但根据原始鼠标的移动来移动它 我知道如何跟踪鼠标的移动 但我不知道如何绘制 重绘鼠标指针 有人可以帮忙吗 您可以使用以下代码 CURSORINFO
  • 如果 f:viewParam / converter 返回 null,如何将用户发送到 404 页面?

    假设您有一个带有视图参数的页面 例如 widgets widgetId 1
  • 这段代码能确保我从套接字读取我想要的所有内容吗?

    执行 Socket Receive byte 将从缓冲区中获取字节 但如果预期的数据相当大 则所有字节可能尚未在缓冲区中 这将给我一个仅部分填充的字节数组 这段代码能确保我读入我想要的所有内容吗 sock Receive message b
  • RDD 谱系何时创建?如何找到谱系图?

    我正在学习 Apache Spark 并尝试获取 RDD 的谱系图 但我找不到特定谱系何时创建 另外 哪里可以找到 RDD 的谱系 RDD谱系是每次应用转换时创建和扩展的分布式计算的逻辑执行计划any RDD 请注意执行操作后发生的 逻辑
  • 如何导入同目录或子目录中的类?

    我有一个目录 其中存储了所有 py files bin main py user py where class User resides dir py where class Dir resides 我想使用来自的课程user py and
  • GoF Factory 的命名约定?

    该模式使用抽象工厂 然后使用工厂的实现 我确信这两个类有一个标准的命名约定 但我不知道它是什么 例如 public abstract class ChocolateFactory public class MyChocolateFactor