Sie versuchen, eine Produkthierarchie zu modellieren, in der ein bestimmtes Produkt seine eigenen spezifischen Eigenschaften haben kann und aus Standard-Unterprodukten besteht. Dies ist in der Tat ein Beispiel für das Zusammensetzungsmuster. Ich empfehle, eine Basisschnittstelle für jede Produktkomponente einzuführen und dann spezifische Schnittstellen für Telefon-, MP3-Player- und Smartphone-Produkte zu schaffen. Im traditionellen Kompositionsmuster kann jeder Knoten eine willkürliche Liste von Komponenten enthalten, zu denen Unterkomponenten hinzugefügt oder entfernt werden können. In Ihrem Datenmodell erscheint es jedoch sinnvoller, für jeden spezifischen Produkttyp seine genauen untergeordneten Elemente anzugeben und dann anzugeben eine generische Methode, um über sie zu iterieren. Dies ermöglicht, dass spezifische (Teil-) Komponenten eines bestimmten Typs/Interface in der gesamten Produkthierarchie leicht abfragbar sein können.
Ich habe auch eine Schnittstelle für ein GPS-Produkt eingeführt, da alle neuen Telefone eingebaute GPS-Empfänger enthalten - nur um zu zeigen, wie man mit rekursiven Hierarchien von Komponenten arbeitet.
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; }
}
Diese Schnittstellen können dann durch einen parallelen Satz von Klassen implementiert werden:
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")
{
}
}
Als neue Produktkomponententypen (oder Unterklasse) hinzugefügt werden, sie die „ChildComponents“ ihrer Eltern außer Kraft setzen und Rückkehr ihre domänenspezifischen Kinder.
Anschließend können Sie (rekursiv) die Produkthierarchie nach Komponenten eines bestimmten Typs für Ihre Verwendung abfragen. Zum Beispiel:
var accuracy = smartPhone.UniqueProductComponent<IGps>().AltitudeAccuracy
oder
bool hasPhone = (component.UniqueProductComponent<ITelephone>() != null)
Diese Kombination von Verallgemeinerung und Zusammensetzung vermeidet Code duplizieren, während explizite machen die Art der Unterkomponenten, die in einem bestimmten Produkt gefunden werden sollte. Außerdem wird vermieden, dass alle übergeordneten Produkte die Schnittstellen ihrer Standard-Kinder übernehmen und alle Anrufe an sie weiterleiten.
Was genau wollen Sie erreichen? Möchtest du, dass 'TelephoneMP3' auf 'ist MP3' und'ist Telefon' zurück? –
Ja, das ist das Ziel – IonR
Raten Sie Am Ende möchten Sie, dass TelephoneMp3 entweder Mp3 oder ein Telefon ist, aber nicht beides? – Rangesh