[英]How do I use composition with inheritance?
我將嘗試在一個簡單的例子中提出我的問題......
假設我有一個抽象的基類Car。 Car有一個基本的Engine對象。 我在抽象Car類中有一個方法StartEngine(),它將引擎的起始委托給Engine對象。
如何允許Car的子類(如法拉利)將Engine對象聲明為特定類型的引擎(例如,TurboEngine)? 我需要另一個Car class(TurboCar)嗎?
我正在繼承一個普通的舊Engine對象,我不能在我的Car子類中重新聲明(或覆蓋)它作為TurboEngine。
編輯:我知道我可以在我的法拉利課程中將任何Engine的子類插入myEngine參考中...但是我怎樣才能調用只有TurboEngine暴露的方法? 因為myEngine是作為基礎引擎繼承的,所以沒有包含任何turbo內容。
謝謝!
抽象工廠模式正是針對這個問題。 Google GoF抽象工廠{您的首選語言}
在下文中,請注意如何使用具體工廠生成“完整”對象(enzo,civic),或者您可以使用它們來生成相關對象的“族”(CarbonFrame + TurboEngine,WeakFrame + WeakEngine)。 最終,您總是會得到一個Car對象,它響應使用類型特定行為的accele()。
using System;
abstract class CarFactory
{
public static CarFactory FactoryFor(string manufacturer){
switch(manufacturer){
case "Ferrari" : return new FerrariFactory();
case "Honda" : return new HondaFactory();
default:
throw new ArgumentException("Unknown car manufacturer. Please bailout industry.");
}
}
public abstract Car createCar();
public abstract Engine createEngine();
public abstract Frame createFrame();
}
class FerrariFactory : CarFactory
{
public override Car createCar()
{
return new Ferrari(createEngine(), createFrame());
}
public override Engine createEngine()
{
return new TurboEngine();
}
public override Frame createFrame()
{
return new CarbonFrame();
}
}
class HondaFactory : CarFactory
{
public override Car createCar()
{
return new Honda(createEngine(), createFrame());
}
public override Engine createEngine()
{
return new WeakEngine();
}
public override Frame createFrame()
{
return new WeakFrame();
}
}
abstract class Car
{
private Engine engine;
private Frame frame;
public Car(Engine engine, Frame frame)
{
this.engine = engine;
this.frame = frame;
}
public void accelerate()
{
engine.setThrottle(1.0f);
frame.respondToSpeed();
}
}
class Ferrari : Car
{
public Ferrari(Engine engine, Frame frame) : base(engine, frame)
{
Console.WriteLine("Setting sticker price to $250K");
}
}
class Honda : Car
{
public Honda(Engine engine, Frame frame) : base(engine, frame)
{
Console.WriteLine("Setting sticker price to $25K");
}
}
class KitCar : Car
{
public KitCar(String name, Engine engine, Frame frame)
: base(engine, frame)
{
Console.WriteLine("Going out in the garage and building myself a " + name);
}
}
abstract class Engine
{
public void setThrottle(float percent)
{
Console.WriteLine("Stomping on accelerator!");
typeSpecificAcceleration();
}
protected abstract void typeSpecificAcceleration();
}
class TurboEngine : Engine
{
protected override void typeSpecificAcceleration()
{
Console.WriteLine("Activating turbo");
Console.WriteLine("Making noise like Barry White gargling wasps");
}
}
class WeakEngine : Engine
{
protected override void typeSpecificAcceleration()
{
Console.WriteLine("Provoking hamster to run faster");
Console.WriteLine("Whining like a dentist's drill");
}
}
abstract class Frame
{
public abstract void respondToSpeed();
}
class CarbonFrame : Frame
{
public override void respondToSpeed()
{
Console.WriteLine("Activating active suspension and extending spoilers");
}
}
class WeakFrame : Frame
{
public override void respondToSpeed()
{
Console.WriteLine("Loosening bolts and vibrating");
}
}
class TestClass
{
public static void Main()
{
CarFactory ferrariFactory = CarFactory.FactoryFor("Ferrari");
Car enzo = ferrariFactory.createCar();
enzo.accelerate();
Console.WriteLine("---");
CarFactory hondaFactory = CarFactory.FactoryFor("Honda");
Car civic = hondaFactory.createCar();
civic.accelerate();
Console.WriteLine("---");
Frame frame = hondaFactory.createFrame();
Engine engine = ferrariFactory.createEngine();
Car kitCar = new KitCar("Shaker", engine, frame);
kitCar.accelerate();
Console.WriteLine("---");
Car kitCar2 = new KitCar("LooksGreatGoesSlow", hondaFactory.createEngine(), ferrariFactory.createFrame());
kitCar2.accelerate();
}
}
只要TurboEngine是Engine的子類,就沒有必要指定Car的子類來擁有TurboEngine。 您只需將TurboEngine的實例指定為法拉利的引擎即可。 你甚至可以在法拉利里放置DieselEngine。 他們都只是發動機。
汽車有引擎。 TurboEngine是一個引擎。 汽車可以配備TurboEngine或DieselEngine或FlintstonesEngine。 他們都是發動機。
如果要限制Car子類中Engine的類型(SportsCar中沒有LawnMowerEngine),可以將其聲明為Engine並將其限制在setter方法中。
Car具有Engine關系並不限制Engine的適用子類。
您始終可以使用受保護的摘要。 公共“開始”將調用受保護的(在抽象類中將是ovveride)。 這樣調用者只能看到Start()而不是StartEngine()。
abstract class Car {
private Engine engine;
public Car() {
this.engine = new Engine();
}
protected Car(Engine engine) {
this.engine = engine;
}
public void Start()
{
this.StartEngine();
}
protected abstract void StartEngine();
}
public class Ferrari : Car
{
public Ferrari() {
}
protected override void StartEngine()
{
Console.WriteLine("TURBO ENABLE!!!");
}
}
- 使用它的方式:
Car c = new Ferrari();
c.Start();
根據您的特定語言語義,有幾種方法可以執行此操作。 關閉袖口我最初的想法是提供一個受保護的構造函數:
public class Car {
private Engine engine;
public Car() {
this(new Engine());
}
protected Car(Engine engine) {
this.engine = engine;
}
public void start() {
this.engine.start();
}
}
public class Ferrari {
public Ferrari() {
super(new TurboEngine());
}
}
我認為這會奏效。
public class Car
{
private Engine engine;
public virtual Engine CarEngine
{
get { return engine;}
}
public StartEngine()
{
CarEngine.Start();
}
}
public class Engine
{
public virtual void Start()
{
Console.Writeline("Vroom");
}
}
public class TurboEngine : Engine
{
public override void Start()
{
Console.Writeline("Vroom pSHHHHHHH");
}
// TurboEngine Only method
public double BoostPressure()
{
}
}
public class Ferrari : Car
{
private TurboEngine engine;
public override Engine CarEngine
{
return engine;
}
}
Ferrari = car new Ferrari();
// Will call Start on TurboEngine()
car.StartEngine();
// Upcast to get TurboEngine stuff
Console.WriteLine(car.CarEngine as TurboEngine).BoostPressure();
你的語言是否有泛型? 在Java中,我可以這樣做:
class Engine {}
abstract class Car<E extends Engine>
{
private E engine;
public E getEngine() { return engine; }
}
class TurboEngine extends Engine {}
class Ferrari extends Car<TurboEngine>
{
// Ferrari now has a method with this signature:
// public TurboEngine getEngine() {}
}
我確信在C#中有類似的東西。 然后,您可以將法拉利的實例視為法拉利子類的實例(使用getEngine返回TurboEngine)或作為Car超類的實例(當getEngine將返回引擎時)。
您可以使用C#generics來獲取您正在尋找的內容。
使用泛型的區別在於你的Ferrari
“知道”它的Engine
是一個TurboEngine
,而Car
類不需要知道任何新的 - 只有EngineType
是一個Engine
。
class Program
{
static void Main(string[] args)
{
Ferrari ferarri = new Ferrari();
ferarri.Start();
ferarri.Boost();
}
}
public class Car<EngineType> where EngineType : Engine, new()
{
protected EngineType engine;
public Car()
{
this.CreateEngine();
}
protected void CreateEngine()
{
this.engine = new EngineType();
}
public void Start()
{
engine.Start();
}
}
public class Ferrari : Car<TurboEngine>
{
public void Boost()
{
engine.Boost();
}
}
public class Engine
{
public virtual void Start()
{
Console.WriteLine("Vroom!");
}
}
public class TurboEngine : Engine
{
public void Boost()
{
Console.WriteLine("Hang on to your teeth...");
}
public override void Start()
{
Console.WriteLine("VROOOOM! VROOOOM!");
}
}
據我了解你的(更新的)問題,如果你想在它上面調用TurboEngine
方法,你將不得不將汽車的引擎轉換為TurboEngine
類型。 在你調用這些方法之前,這會導致大量的檢查以確定你所擁有的汽車是否有TurboEngine
,但這就是你得到的。 不知道這輛車實際上代表什么,我想不出你有什么理由不能讓發動機和渦輪增壓發動機共用相同的接口 - 渦輪機是否有新的方法支持,或者它只是做同樣的事情也不同 - 但我想這個比喻遲早會崩潰。
有很多方法可以做到。
我傾向於在Car
上使用setEngine()
方法,然后讓Ferrari
構造函數調用setEngine()
並傳入TurboEngine
的實例。
不要在界面中暴露你的類的內部 - 換句話說,Car的公共方法應該是Start,而不是StartEngine
如果你想強加一個內部結構(比如只有1個引擎)那么你需要另一個可以專門化的抽象/基類引擎。
然后你可以通過將m_engine成員設置為Engine等人的運動子類來構建一輛跑車
編輯:請注意,在現實世界中,渦輪增壓器不是發動機的一部分,它是發動機的附加裝置,具有自己的控制界面......但是如果你想在你的法拉利發動機中加入這樣的東西,沒關系,只需在SportsCar子類中進行向上翻轉,使您的基礎引擎成為TurboEngine
但是保持組件分離會更好的建模 - 這樣你可以升級你的渦輪增壓器(例如雙進氣口和單進氣口),而無需更換整個發動機!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.