Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/jsf-2/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 多态性设计问题_C#_.net_Oop_Design Patterns_Polymorphism - Fatal编程技术网

C# 多态性设计问题

C# 多态性设计问题,c#,.net,oop,design-patterns,polymorphism,C#,.net,Oop,Design Patterns,Polymorphism,首先,很抱歉问了这么长的问题,但我不能再写得更短了:) 现实世界的例子:我们有一大卷纸,上面印着小的“贴纸”。每个贴纸都有一个代码。代码的前两个字母告诉我们这是什么类型的贴纸(代表新卷的贴纸、代表当前卷结束的贴纸、应该交给质量控制部门的贴纸……但大多数都是普通的枚举贴纸) 例如,带有代码XX0001的标签意味着,在它之后应该只有正常的枚举代码(如NN0001到NN9999),始终是相同的数字。代码QC0001告诉我们,接下来的10个代码(从QC0001到QC0010)应该转到质量控制 我设计的应

首先,很抱歉问了这么长的问题,但我不能再写得更短了:)

现实世界的例子:我们有一大卷纸,上面印着小的“贴纸”。每个贴纸都有一个代码。代码的前两个字母告诉我们这是什么类型的贴纸(代表新卷的贴纸、代表当前卷结束的贴纸、应该交给质量控制部门的贴纸……但大多数都是普通的枚举贴纸)

例如,带有代码XX0001的标签意味着,在它之后应该只有正常的枚举代码(如NN0001到NN9999),始终是相同的数字。代码QC0001告诉我们,接下来的10个代码(从QC0001到QC0010)应该转到质量控制

我设计的应用程序使每种类型的贴纸都有自己的类别-
普通贴纸
不良贴纸
控制贴纸
质量贴纸
,。。。它们都继承自一个SticerBase类,该类包含它们的一些公共数据(扫描质量、扫描日期和时间、代码内容)。这些类的实例在静态解析器类中创建,该类检查代码并将适当的对象返回给我们

这一切正常,但现在我停下来了。我还有一个
Roll
类,它有一组标签,作为
List实现。
这个类有一个公共的
AddSticker(StickerBase)
方法,我们可以用它将标签添加到卷中。但是这个方法应该包含一些逻辑,例如,如果我们得到代码XX001,那么接下来的9999标签应该是从NN0001到NN9999。我在这里看到的唯一选择是根据贴纸的类型做出决定,如:

public void AddSticker(StickerBase sticker)
{
    if (sticker.GetType().Equals(typeof(StickerNewRoll)))
    {
        // Next 9999 sticker should be in the form of NN0001 to NN9999
    }

    if (sticker.GetType().Equals(typeof(EnumeratedSticker)))
    {
        // Add 9999 stickers to the list, other business logic...
    }

    if (sticker.GetType().Equals(typeof(QualitySticker)))
    {
        // Stop the machine and notify the worker
    }
}
如果这是正确的方法,我会非常惊讶。有什么想法吗


编辑-可能的解决方案:因为对于每个贴纸,我知道下一个贴纸应该是什么样子,所以我可以向每个
贴纸
类添加新方法
公共贴纸NextStickerShouldLookLike()
方法。在验证逻辑(类似于)中,我可以检查当前标签是否与以前的标签相同。NextStickerShouldLookLike()
。Validate方法将有两个输入参数—current和previous sticker。

无论如何,我会用这种方式重写它:

if (sticker is StickerNewRoll)
{
    // Next 9999 sticker should be in the form of NN0001 to NN9999
}
else if (sticker is EnumeratedSticker)
{
    // Add 9999 stickers to the list, other business logic...
}
else if (sticker is QualitySticker)
{
    // Stop the machine and notify the worker
}

创建一个虚拟函数GetNextStickerLabel();并在任何贴纸类型中实现不同。作为一般规则,如果需要询问对象的类型并对其作出决定(如果),则设计中存在错误。

是否要在一次移动中添加与特定标签相关联的标签集,还是要验证添加的标签是否符合最新特殊标签设置的约束

在第一种情况下,您可以将多态的
GetAssociatedStickers()
方法添加到您的贴纸类中,该方法将从NN0001到NN9999的贴纸集返回到代码为XX001的贴纸,以此类推。然后您可以将该贴纸集添加到控件贴纸之后

更新 对于验证,您可以在标签类中有一个新的接口
StickValidator
,以及一个方法
GetValidator
。特殊标签将返回正确的验证器对象(可以作为匿名类或内部类实现),而常规标签将返回
null
。然后可以修改
AddSticker
,使其看起来像

public void AddSticker(StickerBase sticker)
{
    if (sticker.GetValidator() != null)
    {
        this.validator = sticker.GetValidator();
        // add the sticker to the list
    }
    else
    {
        if (this.validator == null || this.validator.validate(sticker))
        {
            // add the sticker to the list
        }
        else
        {
            // set error state
        }
    }
}

基于类型使用条件表达式是一种反模式,您应该尽量避免这种情况,这是正确的。例如,问题之一是,无论何时创建新的
StickerBase
子类,都必须更新此方法。另一个问题是,方法签名表示调用者可以传递
标签库
的任何实现,但实际上只支持少数实现

如果可能,将您的逻辑放在
标签中。在
base
类中有一个抽象方法,并在子类中重写。这样,您只需要在
AddSticker
方法中调用一个方法,而不必知道添加了什么类型的标签


如果这是不可能的,并且你真的需要让你的代码处理你的<代码>卷类中的不同类型的贴纸,你可以考虑查看这个模式。尽量避免这种情况,如果可能的话,使用第一种方法

你有一个很好的选择来解决这个问题

可以实现标签种类和属性的枚举

public enum StickerKinds
{
     NewRoll, Enumerated, Quality
}

public class StickedAttribute : Attribute
{
      public StickedAttribute(StickerKinds kind)
      {
           _kind = kind;
      }

      private readonly StickerKinds _kind;

      public StickerKinds Kind 
      {
           get { return _kind; }
      }
}
现在您可以有一个名为“StickerLabeler”的类,它实现了一个名为“NextLabel”的方法,该方法接受StickerTypes类型的输入参数,并返回一个字符串,将整个标签表示为文本

最后,还有一种易于检索属性的扩展方法:

public static class ObjectExtensions
{
    public static TAttribute GetAttribute<TAttribute>(this object source)
        where TAttribute : Attribute
    {
        if (source != null)
        {
            object[] attributeSearchResult = source.GetType().GetCustomAttributes(typeof(TAttribute), true);

            if (attributeSearchResult.Length > 0)
            {
                return (TAttribute)attributeSearchResult.Single();
            }
            else
            {
                return default(TAttribute);
            }
        }
        else
        {
            return default(TAttribute);
        }
    }
}

更新:事实上,没有理由再子类化了。您可以有一个贴纸类,该属性将决定贴纸的类型

更新2:你可以做另一个增强。在Sticker类“Kind”中实现一个只读属性,该属性可以读取该属性,并将返回所谓属性的StickKinds枚举值,因此,现在您的代码可以更干净:

public void AddSticker(StickerBase sticker)
{
    sticker.Label = StickerLabeler.NextLabel(sticker.Kind);

    // TODO: Implement code to add the sticker to the store.
}

更新3:Andreas——我的答案的评论者——让我认为您可能需要子类化,因为每种标签都有自己的属性,所谓的属性将应用于这些派生类

我可以在其他答案中看到激烈的争论,但我不确定如果不深入研究您的示例,是否有可能找到一个好的解决方案

首先,您有三个类,Roll、创建标签的解析器和标签本身(分为派生类)。也许还有另一个类实现了一些您没有提到的业务逻辑?也许值得描述一下

第一个问题是: 考虑到某个类负责attachi
[Sticked(StickerKinds.NewRoll)]
public class StickerNewRoll
{ 
...
}
public void AddSticker(StickerBase sticker)
{
    sticker.Label = StickerLabeler.NextLabel(sticker.Kind);

    // TODO: Implement code to add the sticker to the store.
}
public interface IStickerVisitor
{
    void Visit(NewRollSticker sticker);
    void Visit(EnumeratedSticker sticker);
    void Visit(QualitySticker sticker);
    //need a method for every kind of sticker here
}
public abstract void Accept(IStickerVisitor visitor);
public abstract void Accept(IStickerVisitor visitor)
{
     visitor.Visit(this);
}
public class StickerRollerVisitor : IStickerVisitor
{
    private RollList rollList;
    public StickerRollerVisitor(RollList list)
    {
        this.rollList = list;
    }
    public void Visit(NewRollSticker sticker)
    {
        // Next 9999 sticker should be in the form of NN0001 to NN9999
    }
    public void Visit(EnumeratedSticker sticker)
    {
        // Add 9999 stickers to the list, other business logic...
    }
    public void Visit(QualitySticker sticker)
    {
        // Stop the machine and notify the worker
    }
}
private StickerRollerVisitor rollListStickerVisitor;
public void AddSticker(StickerBase sticker)
{
    sticker.Accept(rollListStickerVisitor)
}