2

已更新我已更新示例以更好地说明我的问题。我意识到它遗漏了一个特定点 - 即该CreateLabel()方法总是采用标签类型,因此工厂可以决定创建哪种类型的标签。问题是,它可能需要获取更多或更少的信息,具体取决于它想要返回的标签类型。

我有一个工厂类,它返回代表要发送到打印机的标签的对象。

工厂类如下所示:

public class LargeLabel : ILabel
{
    public string TrackingReference { get; private set; }

    public LargeLabel(string trackingReference)
    {
        TrackingReference = trackingReference;
    }
}

public class SmallLabel : ILabel
{
    public string TrackingReference { get; private set; }

    public SmallLabel(string trackingReference)
    {
        TrackingReference = trackingReference;
    }
}

public class LabelFactory
{
    public ILabel CreateLabel(LabelType labelType, string trackingReference)
    {
        switch (labelType)
        {
            case LabelType.Small:
                return new SmallLabel(trackingReference);
            case LabelType.Large:
                return new LargeLabel(trackingReference);
        }
    }
}

假设我创建了一个名为 CustomLabel 的新标签类型。我想从工厂退回这个,但它需要一些额外的数据:

public class CustomLabel : ILabel
{
    public string TrackingReference { get; private set; }
    public string CustomText { get; private set; }

    public CustomLabel(string trackingReference, string customText)
    {
        TrackingReference = trackingReference;
        CustomText = customText;
    }
}

这意味着我的工厂方法必须改变:

public class LabelFactory
{
    public ILabel CreateLabel(LabelType labelType, string trackingReference, string customText)
    {
        switch (labelType)
        {
            case LabelType.Small:
                return new SmallLabel(trackingReference);
            case LabelType.Large:
                return new LargeLabel(trackingReference);
            case LabelType.Custom:
                return new CustomLabel(trackingReference, customText);
        }
    }
}

我不喜欢这样,因为工厂现在需要满足最小公分母,但同时 CustomLabel 类需要获取自定义文本值。我可以提供额外的工厂方法作为替代,但我想强制执行 CustomLabel 需要该值的事实,否则它只会被赋予空字符串。

实现此方案的正确方法是什么?

4

8 回答 8

4

那么,你想怎么调用工厂方法呢?

专注于你希望如何使用你的 API,实现通常会很清楚。如果您将 API 的所需结果编写为单元测试,这将变得更加容易。

过载可能是正确的做法,但这实际上取决于您希望如何使用工厂。

于 2009-03-30T16:45:28.480 回答
2

仅仅使用工厂方法来决定你需要什么标签怎么样?

public class LabelFactory {
    public ILabel CreateLabel(string trackingReference, string customText) {
        return new CustomLabel(trackingReference, customText);
    }

    public ILabel CreateLabel(String trackingReference) {
        return new BasicLabel(trackingReference);
    }
}

您的工厂仍然需要了解每种类型(尽管您可以通过接口实现动态加载),但客户端需要知道的很少 - 根据提供的数据,工厂生成正确的实现。

这是您描述的简单问题的简单解决方案。我认为这个问题是对更复杂问题的过度简化,但在不知道你真正的问题是什么的情况下,我宁愿不设计一个过于复杂的解决方案。

于 2009-03-30T17:39:00.287 回答
1

这可能表明工厂模式不是最适合您的。但是,如果您确实需要或希望坚持使用它,我建议创建可以传递给工厂的初始化类/结构,而不是字符串。无论您是想使用基本信息类的各种子类(基本上创建一个模仿标签类的初始化类层次结构)还是一个包含所有信息的类都取决于您。

于 2009-03-30T16:44:37.640 回答
1

您应该尝试使用配置类并将其实例传递给工厂。配置类将构建一个层次结构,其中对于您期望从工厂获得的每个结果都存在一个特殊的配置类。每个配置类都捕获工厂结果的特定属性。

对于您给出的示例,我将编写一个 BasicLabelConfiguration 和一个从它派生的 CustomLabelConfiguration。BasicLabelConfiguration 捕获跟踪引用,而 CustomLabelConfiguration 捕获自定义文本。

最后工厂根据传递的配置对象的类型做出决定。


下面是代码示例:

public class BasicLabelConfiguration
{
  public BasicLabelConfiguration()
  {
  }

  public string TrackingReference { get; set; }
}

public class CustomLabelConfiguration : BasicLabelConfiguration
{
  public CustomLabelConfiguration()
  {
  }

  public string CustomText { get; set; }
}

public class LabelFactory
{
  public ILabel CreateLabel(BasicLabelConfiguration configuration)
  {
    // Possibly make decision from configuration
    CustomLabelConfiguration clc = configuration as CustomLabelConfiguration;
    if (clc != null)
    {
      return new CustomLabel(clc.TrackingReference, clc.CustomText);
    }
    else
    {
      return new BasicLabel(configuration.TrackingReference);
    }
  }
}

最后你会像这样使用工厂:

// Create basic label
ILabel label = factory.CreateLabel(new BasicLabelConfiguration 
{
  TrackingReference = "the reference"
});

或者

// Create basic label
ILabel label = factory.CreateLabel(new CustomLabelConfiguration 
{
  TrackingReference = "the reference",
  CustomText = "The custom text"
});
于 2009-03-30T16:45:49.510 回答
1

如果没有更多信息,很难给出任何建议,但假设工厂模式是您真正需要的,您可以尝试以下方法:

将所需的参数打包到某种属性映射中(例如字符串到字符串的映射),并将其作为参数传递给工厂的 create 方法。使用众所周知的标签作为映射中的键,允许专业工厂根据自己的喜好提取和解释映射值。

这至少可以让您暂时维护一个单一的工厂接口,如果(或何时)您注意到工厂模式在这里不正确,则可以推迟处理架构问题。

(哦,如果您真的想在这里使用工厂模式,我强烈建议您将其设置为可插入的,以避免必须为每种新标签类型修改工厂)。

于 2009-03-30T17:27:50.250 回答
1

您正试图将模式强加到它不适合的场景中。我建议放弃那个特定的模式和重点,而不是让最简单的解决方案成为可能。

我认为在这种情况下,我将只有一个类,标签,它有一个用于自定义文本的文本字段,该文本字段通常为空/空,但如果需要自定义标签,可以设置哪个。它很简单,不言自明,不会给您的维护程序员带来任何噩梦。

public class Label
{
    public Label(string trackingReference) : this(trackingReference, string.Empty)
    {
    }

    public Label(string trackingReference, string customText)
    {
        CustomText = customText;
    }

    public string CustomText ( get; private set; }

    public bool IsCustom
    {
        get
        {
            return !string.IsNullOrEmpty(CustomText);
        }
    }
}
于 2009-03-30T17:55:14.070 回答
1

回答问题更新后的更新 - 见下文

我仍然认为您使用工厂模式是正确的,并且正确地重载了​​ CreateLabel 方法;但我认为在将 LabelType 传递给 CreateLabel 方法时,您错过了使用工厂模式的要点。

要点:工厂模式的全部目的是封装选择要实例化和返回的具体子类的逻辑。调用代码不应该告诉工厂要实例化哪种类型。这样做的好处是,调用工厂的代码因此不受将来对该逻辑的更改,也不受向工厂添加新的具体子类的影响。您的所有调用代码需要依赖的是工厂,以及从 CreateLabel 返回的接口类型。

您调用工厂时代码中的逻辑当前必须类似于此伪代码...

// Need to create a label now
ILabel label;
if(we need to create a small label)
{
   label = factory.CreateLabel(LabelType.SmallLabel, "ref1");
}
else if(we need to create a large label)
{
   label = factory.CreateLabel(LabelType.LargeLabel, "ref1");
}
else if(we need to create a custom label)
{
   label = factory.CreateLabel(LabelType.CustomLabel, "ref1", "Custom text")
}

...所以你明确告诉工厂要创建什么。这很糟糕,因为每次将新标签类型添加到系统时,您都需要...

  1. 更改工厂代码以处理新的 LabelType 值
  2. 去工厂调用的任何地方添加一个新的 else-if

但是,如果您将选择 LabelType 值的逻辑移到您的工厂中,则可以避免这种情况。逻辑与其他所有内容一起封装在工厂中。如果将新类型的标签添加到您的系统中,您只需更改出厂设置。所有调用工厂的现有代码保持不变,没有重大更改。

您当前的调用代码使用什么数据来决定是否需要大标签或小标签?那条数据应该传递给工厂的 CreateLabel() 方法。

您的工厂和标签类别可能如下所示...

// Unchanged
public class BasicLabel: ILabel
{
    public LabelSize Size {get; private set}
    public string TrackingReference { get; private set; }

    public SmallLabel(LabelSize size, string trackingReference)
    {
        Size = size;
        TrackingReference = trackingReference;
    }
}



// ADDED THE NULL OR EMPTY CHECK
public class CustomLabel : ILabel
{
    public string TrackingReference { get; private set; }
    public string CustomText { get; private set; }

    public CustomLabel(string trackingReference, string customText)
    {
        TrackingReference = trackingReference;
        if(customText.IsNullOrEmpty()){
           throw new SomeException();
        }
        CustomText = customText;
    }
}



public class LabelFactory
{
    public ILabel CreateLabel(string trackingReference, LabelSize labelSize)
    {
         return new BasicLabel(labelSize, trackingReference);
    }

    public ILabel CreateLabel(string trackingReference, string customText)
    {
         return new CustomLabel(trackingReference, customText);
    }
}

我希望这是有帮助的。

于 2009-04-01T22:03:56.033 回答
0

从阅读您的问题看来,您的 UI 会收集信息,然后使用工厂创建适当的标签。我们在我开发的 CAD/CAM 应用程序中使用了不同的方法。

在启动期间,我的应用程序使用工厂方法创建标签主列表。我的一些标签具有初始化参数,因为它们是彼此的变体。例如,我们有三种类型的扁平零件标签。而其他的具有用户定义的或在设置时未知的参数。

在第一种情况下,初始化是在工厂方法中处理的。因此,我创建了三个传入所需参数的 FlatPartLabel 实例。

在第二种情况下,标签接口有一个配置选项。这由标签打印机对话框调用以填充设置面板。在您的情况下,这是传入跟踪参考和 CustomText 的地方。

我的标签接口还为每个标签类型返回一个唯一的 ID。如果我有一个特定的命令来处理该类型的标签,那么我将遍历我的应用程序中的标签列表,找到与 ID 匹配的标签,将其转换为特定类型的标签,然后对其进行配置。当用户只想为特定的平面部分打印一个标签时,我们会这样做。

这样做意味着您可以对标签所需的参数进行任意复杂化,并且不会给工厂带来不必要的参数负担。

于 2009-03-30T19:10:53.780 回答