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