如何将组合与继承结合使用?
-
07-07-2019 - |
题
我将尝试在一个简单的例子中提出我的问题......
假设我有一个抽象基类 Car。汽车有一个基本的 Engine 对象。我在抽象 Car 类中有一个 StartEngine() 方法,它将引擎的启动委托给 Engine 对象。
如何允许 Car 的子类(如 Ferrari)将 Engine 对象声明为特定类型的发动机(例如 TurboEngine)?我需要另一个 Car 类 (TurboCar) 吗?
我继承了一个普通的旧 Engine 对象,并且无法在我的 Car 子类中将其重新声明(或重写)为 TurboEngine。
编辑: 我知道我可以将 Engine 的任何子类插入到我的 Ferrari 类中的 myEngine 引用中...但是我如何调用只有 TurboEngine 公开的方法?因为 myEngine 是作为基础 Engine 继承的,所以不包含任何 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 的一个实例作为您的 Ferrari 的引擎。您甚至可以在您的法拉利中安装柴油发动机。他们都只是引擎。
汽车有发动机。TurboEngine 是一个引擎。汽车可以有涡轮发动机、柴油发动机或燧石发动机。他们都是引擎。
如果您想限制 Car 子类中的 Engine 类型(SportsCar 中没有 LawnMowerEngine),您可以将其声明为 Engine 并在 setter 方法中限制它。
Car 与 Engine 的关系并不限制 Engine 的适用子类。
您始终可以使用受保护的摘要。公众“开始”将调用protected(在抽象类中将是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 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();
您可以使用C#generics来获取您所需的内容。
使用泛型的区别在于您的法拉利
“知道”它的 Engine
是一个 TurboEngine
,而 Car
类不必知道任何新的 EngineType
is-an 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
,但这就是您所获得的。不知道这辆车实际上代表什么,我想不出你有什么理由不能让发动机和涡轮增压发动机共用相同的接口 - 涡轮机是否有新的方法支持,或者它只是做同样的事情也不同 - 但我想这个比喻迟早会崩溃。
你的语言是否有泛型?在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将返回引擎时)。
根据您的特定语言语义,有几种方法可以执行此操作。关闭袖口我最初的想法是提供受保护的构造函数:
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());
}
}
不要在界面中暴露你的类的内部 - 换句话说,Car的公共方法应该是Start,而不是StartEngine
如果你想强加一个内部结构(比如只有1个引擎)那么你需要另一个可以专门化的抽象/基类引擎。
然后你可以通过将m_engine成员设置为Engine的运动子类来构建一辆跑车,等等
编辑:请注意,在现实世界中,涡轮增压器不是引擎的一部分,它是引擎的附加组件,具有自己的控制界面......但是如果你想在你的内容中包含这样的东西法拉利引擎,没关系,只是在SportsCar子类中向上翻转,使你的基础引擎成为TurboEngine
但是将组件分开是更好的建模方式 - 这样你可以升级你的涡轮增压器(例如双进气口和单进气口),而无需更换整个发动机!
有很多方法可以做到。
我赞成在 Car
上使用 setEngine()
方法,然后让 Ferrari
构造函数调用 setEngine()
并传入 TurboEngine
的实例。