您正在尝试对产品层次结构进行建模,其中给定的产品可能具有自己的特定属性,并且由标准子产品组成。这确实是构图模式的一个例子。我建议为任何产品组件引入基本接口,然后为电话、MP3 播放器和智能手机产品创建特定接口。
在传统的组合模式中,每个节点可能包含一个任意的组件列表,可以向其中添加或删除子组件,但是在您的数据模型中,对于每种特定类型的产品来指定其精确的子节点似乎更有用,然后提供一个通用方法来遍历它们。这允许在整个产品层次结构中轻松查询指定类型/接口的特定(子)组件。
我还介绍了 GPS 产品的接口,因为所有新手机都包含内置 GPS 接收器——只是为了说明如何使用组件的递归层次结构。
public interface IProductComponent
{
string Name { get; set; }
IEnumerable<IProductComponent> ChildComponents { get; }
IEnumerable<IProductComponent> WalkAllComponents { get; }
TProductComponent UniqueProductComponent<TProductComponent>() where TProductComponent : class, IProductComponent;
}
public interface ITelephone : IProductComponent
{
IGps Gps { get; }
}
public interface IMp3Player : IProductComponent
{
}
public interface IGps : IProductComponent
{
double AltitudeAccuracy { get; }
}
public interface ISmartPhone : IProductComponent
{
ITelephone Telephone { get; }
IMp3Player Mp3Player { get; }
}
然后可以通过一组并行的类来实现这些接口:
public abstract class ProductComponentBase : IProductComponent
{
string name;
protected ProductComponentBase(string name)
{
this.name = name;
}
#region IProductComponent Members
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
public virtual IEnumerable<IProductComponent> ChildComponents
{
get
{
return Enumerable.Empty<IProductComponent>();
}
}
public IEnumerable<IProductComponent> WalkAllComponents
{
get
{
yield return this;
foreach (var child in ChildComponents)
{
foreach (var subChild in child.WalkAllComponents)
yield return subChild;
}
}
}
public TProductComponent UniqueProductComponent<TProductComponent>() where TProductComponent : class, IProductComponent
{
TProductComponent foundComponent = null;
foreach (var child in WalkAllComponents.OfType<TProductComponent>())
{
if (foundComponent == null)
foundComponent = child;
else
throw new Exception("Duplicate products found of type " + typeof(TProductComponent).Name);
}
return foundComponent;
}
#endregion
}
public class Telephone : ProductComponentBase, ITelephone
{
IGps gps = new Gps();
public Telephone()
: base("telephone")
{
}
#region ITelephone Members
public IGps Gps
{
get
{
return gps;
}
}
#endregion
IEnumerable<IProductComponent> BaseChildComponents
{
get
{
return base.ChildComponents;
}
}
public override IEnumerable<IProductComponent> ChildComponents
{
get
{
if (Gps != null)
yield return Gps;
foreach (var child in BaseChildComponents)
yield return child;
}
}
}
public class Gps : ProductComponentBase, IGps
{
public Gps()
: base("gps")
{
}
#region IGps Members
public double AltitudeAccuracy
{
get { return 100.0; }
}
#endregion
}
public class TelephoneMP3 : ProductComponentBase, ISmartPhone
{
ITelephone telephone;
IMp3Player mp3Player;
public TelephoneMP3()
: base("TelephoneMP3")
{
this.telephone = new Telephone();
this.mp3Player = new MP3();
}
IEnumerable<IProductComponent> BaseChildComponents
{
get
{
return base.ChildComponents;
}
}
public override IEnumerable<IProductComponent> ChildComponents
{
get
{
if (Telephone != null)
yield return Telephone;
if (Mp3Player != null)
yield return Mp3Player;
foreach (var child in BaseChildComponents)
yield return child;
}
}
#region ISmartPhone Members
public ITelephone Telephone
{
get { return telephone; }
}
public IMp3Player Mp3Player
{
get { return mp3Player; }
}
#endregion
}
public class MP3 : ProductComponentBase, IMp3Player
{
public MP3()
: base("mp3Player")
{
}
}
随着新产品组件类型的添加(或子类化),它们将覆盖其父级的“ChildComponents”并返回其特定于域的子级。
完成此操作后,您可以(递归地)查询产品层次结构以查找给定类型的组件以供您使用。例如:
var accuracy = smartPhone.UniqueProductComponent<IGps>().AltitudeAccuracy
或者
bool hasPhone = (component.UniqueProductComponent<ITelephone>() != null)
这种泛化和组合的组合避免了重复代码,同时明确了在任何给定产品中应该找到的子组件的类型。它还避免了让所有高级产品代理其标准子接口的负担,将所有调用传递给它们。