鉴于这个抽象类:
public abstract class File
{
public abstract string Name { get; set; }
public abstract void Add(File newFile);
}可以生成复合的基础:
public class LogFile : File
{
private string _name;
public override string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
public override void Add(File newFile)
{
throw new NotImplementedException();
}
}
public class LogFiles : File
{
private IList<File> _files = new List<File>();
public override string Name
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public override void Add(File newFile)
{
_files.Add(newFile);
}
}开箱即出,就会生成异常代码。大多数情况下,在编写初始代码时,这很方便。在集合类和对象类之间切换时(这主要是我所处理的),您可以很容易地忘记您正在处理的是哪种对象。特别是当变量通常使用抽象类名命名时:
File thisFile = new LogFile {Name = "Monday.log"};
File thatFile = new LogFiles();
thatFile.Add(new LogFile() { Name = "Tuesday.log" });
thatFile.Add(new LogFile() { Name = "Wednesday.log" });
thatFile.Name = "My files"; // Runtime error但是,如果保留此代码,当这些对象在上游被消耗时(作为真正的组合),当使用无参数构造函数并且未实现的属性抛出异常时,可能会出现问题。一个常见的例子是各种形式的序列化:
static void SerializeThis(File fileSet)
{
var json = new JavaScriptSerializer().Serialize(fileSet);
}在这种情况下,可以简单地重构代码以删除异常:
public class LogFile : File
{
private string _name;
public override string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
public override void Add(File newFile)
{
}
}
public class LogFiles : File
{
private IList<File> _files = new List<File>();
public override string Name
{
get
{
return string.Empty;
}
set
{
}
}
public override void Add(File newFile)
{
_files.Add(newFile);
}
}但这消除了派生类的安全保护,并使代码看起来有些粗糙。在我看来,合成的存在理由是两分法:“我不应该关心我是对象还是对象的集合”,并确保每个派生类被正确地使用(作为派生类)。
我情不自禁地认为我错过了一些最基本的东西--有什么想法吗?
另外,一些编码工具(如ReSharper )实现了自动属性,而不是包含异常的getter和setter。虽然这并不完全是万无一失的,但这无疑是一种更好的方法。
发布于 2017-11-23 14:41:44
问题的核心似乎是在每一种情况下,复合材料的消费方式都不够清晰。就目前情况而言,对象和集合类具有明确定义的角色,但它们只是名称上的组合(即它们的定义)。因此,这就需要消费者做一些繁重的工作。
通过引用对象和按类名收集,水域将变得更加混乱。应该使用更微妙的行为来确定对象和集合类。
一种更好的方法是定义某种类型的默认行为,为所有3种用途服务:对象、集合和不可知论者。
一种可能的方法是使基类中的方法和属性变为虚拟的。这避免了必须为永远不会使用的派生类中的方法和属性编写代码的棘手问题(为了完整性,我添加了AllFiles getter ):
public class File
{
protected string _name = string.Empty;
protected IList<File> _allFiles = new List<File>();
public virtual string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
public virtual void Add(File newFile)
{
_allFiles.Add(newFile);
}
public virtual IList<File> AllFiles
{
get
{
return _allFiles;
}
}
}这有若干好处:
首先,派生类比较干净,因为它们只包含所需的重写。
第二,它迫使开发人员在前面全面考虑合成物,而不是将其各部分的总和考虑在内。在开发人员可能无法控制的其他层中使用组合时,这一点尤为重要。
第三,它简单地消除了对消费者不感兴趣的方法的猜测--在规范的抽象类解决方案中,这个持久的问题几乎随处可见。从我第一次使用这个模式开始,这就是我从来没有对过的东西。
https://softwareengineering.stackexchange.com/questions/361168
复制相似问题