具有可以具有不同类子类型的类的工厂模式

时间:2015-01-27 14:42:10

标签: c# design-patterns

我有来自多个组织(警察,消防,办公室)的数据,这些组织需要以不同格式输出。

为实现这一目标,我定义了以下内容(这有点简化):

交易类 -

  1. “成功”指示符 - 布尔值。
  2. “部门类型” - 字符串或枚举。
  3. 可以是任何类型的课程 - 警察,消防或办公室(我的问题就是这个,你会看到)。
  4. GenerateOutput()方法 - 处理文件格式的生成。
  5. 警察课

    1. 年龄 - 字符串
    2. VehicleNumber - Integer
    3. 主管 - 字符串
    4. 火派

      1. 名称 - 字符串
      2. FireEngineNumber - 整数
      3. 县 - Enum
      4. WorkTimings - Enum
      5. Office Class

        1. 年龄 - 字符串
        2. DeskNumber - 整数
        3. 部门 - 字符串
        4. PayScale - Enum
        5. IsManagement - Bool
        6. 正如您所看到的,警察,消防和办公室课程不共享任何共同点,主要用作数据承载实体。我打算使用Factory返回一个适当的泛型(不是C#泛型)Transaction对象与数据(事务对象中包含 Police,Fire或Office 数据),然后将返回的对象传递给策略模式,用于确定每个人需要的文件格式(CSV,Excel或XML;在配置文件中指定)。

          我的问题在于Transaction对象的定义。

          1. “3.”中的班级是什么类型Transaction类需要是什么?每个组织的数据不同,没有共同的成员,我无法为所有人定义公共类。

          2. 整体设计是否合适?我应该考虑哪些其他设计?

          3. 基于彼得的评论如下: 我认为使用泛型可能会有效,但我遇到了一个问题。我想使用工厂返回请求的对象,使用GetTransactionObject,如下所示。什么应该是GetTransactionObject的返回类型来容纳这个。

            类TransactionFactory {

                    Dictionary<string, Type> typeClassLookup;
            
                public TransactionFactory()
                {
                    typeClassLookup = new Dictionary<string, Type>();
                    typeClassLookup.Add("Police", typeof(PoliceData));
                    typeClassLookup.Add("Fire", typeof(FireData));
                }
            
                Transaction<????> GetTransactionObject(string org)
                {
            
                    if( typeClassLookup.TryGetValue(org, out typeValue))
                    {
                        switch (typeValue.ToString())
                        {
                            case "policeData":
                                transactionObject = new Transaction<PoliceData>() { Data = new PoliceData(), params = null};
                            case "FireData":
                                transactionObject = new Transaction<FireData>() {Data = new FireData(), params = null};
                        }
                    }
                    return transactionObject;
            

2 个答案:

答案 0 :(得分:2)

如果类型真的没有任何共同点,那么您不需要明确的基类。 System.Object就足够了,就像许多其他泛型类型一样(即任何缺少约束的泛型类型)。

换句话说,您可以声明为:

class Transaction<T>
{
    public bool Success { get; private set; }
    public T Entity { get; private set; }

    public Transaction(bool success, T entity)
    {
        Success = success;
        Entity = entity;
    }

    public void GenerateOutput() { /* something goes here */ }
}

就个人而言,我会避免添加“部门类型”成员。毕竟,这是从类型参数T隐含的。但是如果你愿意,你可以轻松地将其添加到上面。

如果您发现这些类型确实存在共同点,那么您的Transaction<T>类型需要做的不仅仅是保留其中一种类型的实例(这是它可以做到的所有事情)约束),然后您将能够将该共性放入接口或基类(取决于特定需要),并在Transaction<T>类的约束中指定。

请注意,您不清楚GenerateOutput()要做什么,或者它应该如何运作。但假设您希望输出特定于每个Entity值,在我看来, 是您的“共同点”。即,根本不是Transaction<T>类需要实现该方法,而是每个实体类型。在这种情况下,你有这样的事情:

interface IDepartmentEntity
{
    void GenerateOutput();
}

class Office : IDepartmentEntity
{
    public void GenerateOutput() { /* department-specific logic here */ }
}

// etc.

然后你可以声明:

class Transaction<T> where T : IDepartmentEntity
{
    public bool Success { get; private set; }
    public T Entity { get; private set; }

    public Transaction(bool success, T entity)
    {
        Success = success;
        Entity = entity;
    }

    public void GenerateOutput() { Entity.GenerateOutput(); }
}


修改

Per Prasant的后续编辑,请求GetTransactionObject()的建议......

执行此操作的正确方法取决于调用者和上下文,问题中未提供详细信息。恕我直言,最佳场景是调用者知道类型的地方。这样可以使用泛型的全部功能。

例如:

class TransactionFactory
{
    public Transaction<T> GetTransactionObject<T>()
        where T : IDepartmentEntity, new()
    {
        return new Transaction<T>()
        {
            Data = new T(),
            params = null
        }
    }
}

然后你这样打电话:

Transaction<FireData> transaction = factory.GetTransactionObject<FireData>();

调用者当然已经知道它正在创建的类型,然后可以填写transaction.Data对象的相应属性。

如果无法实现这种方法,那么您需要Transaction<T>本身拥有基类或实现接口。请注意,在我的原始示例中,IDepartmentEntity接口只有一个方法,它与GenerateOutput()类中的Transaction方法相同。

所以,也许,该接口实际上是关于生成输出而不是数据实体。将其称为IDepartmentEntity,而不是IOutputGenerator

在这种情况下,您可能会遇到以下情况:

class Transaction<T> : IOutputGenerator
{
    // all as before
}

class TransactionFactory
{
    public IOutputGenerator GetTransactionObject(string org)
    {
        if( typeClassLookup.TryGetValue(org, out typeValue))
        {
            switch (typeValue.ToString())
            {
                case "policeData":
                    transactionObject = new Transaction<PoliceData>() { Data = new PoliceData(), params = null};
                case "FireData":
                    transactionObject = new Transaction<FireData>() {Data = new FireData(), params = null};
            }
        }
        return transactionObject;
    }
}

这是一个较差的解决方案,因为这意味着调用者只能直接访问IOutputGenerator功能。其他任何事情都需要做一些类型检查和特殊情况代码,这些都应该尽可能地避免。

注意:如果Transaction类型有其他成员,就像GenerateOutput()方法一样,独立于此处包含的类型T,并且哪些会对那些不喜欢的用户有用知道T,然后上面的一个可能的变化是不重用用于特定部门数据类型的接口,而是声明Transaction<T>的基类,当然命名为{{1} },包含与Transaction无关的所有成员。然后返回值可以是T

答案 1 :(得分:0)

  
      
  1. 该课程的类型是什么?#34; 3。&#34; Transaction类需要是什么?
  2.   

要将您的部门类与各种导出类型分离,我建议您使部门类实现一个通用接口。像这样:

public interface Exportable {
    // return a list of attribute names, values, and types to export
    IList<Tuple<String, String, Type>> GetAttributes();
}

例如:

public class Police : Exportable {
    public IList<Tuple<String, String, Type>> GetAttributes() {
        // return list size 3 - attribute info for Age, VehicleNumber, Supervisor
    }
}
  
      
  1. 整体设计是否合适?我应该考虑哪些其他设计?
  2.   

Transaction班级设计似乎不适合这个问题。

考虑一个Export类,每个导出类型都有一个方法,每个方法接收从Exportable接口方法返回的属性。基本概要:

public static class Export {
    public static boolean CSV(IList<Tuple<String, String, Type>> attributes) {
        // export attributes to CSV, return whether succeeded
    }

    public static boolean Excel(IList<Tuple<String, String, Type>> attributes) {
        // export attributes to Excel, return whether succeeded
    }

    // same thing for XML
}