抽象工厂、依赖注入和良好的设计
我正在努力掌握良好系统设计的诀窍。由于良好的系统设计没有硬性规定,因此我请求您给我一些宝贵的建议。我准备了一个想象的系统并为此准备了一个设计。如果您认为这个设计是好是坏,请告诉我。有更好的办法吗?我在解决方案中使用了抽象工厂和依赖注入。
问题:
设计一个在汽车中运行并控制它的系统。要求是为大众高尔夫制作一个控制发动机、电气、变速箱等组件的系统。
高尔夫可以有不同版本,如 BlueMotion、Twist、GT 等,它们使用不同的组件。例如,如果 GT 使用发动机 a 和电气 b,则 BlueMotion 可以使用发动机 c 和电气 d。
不同汽车的发动机、电气设备和其他部件可能相似,也可能完全不同。例如,“Flat 4 CRDI”发动机使用 CRDI 技术进行燃油喷射,并具有一定的减少振动的性能。 “PumpeDuse V6”发动机采用PD喷射及其自己的减振方法,与“Flat4 CRDI”完全不同。差异不仅在于处理振动,还在于发动机行为的大多数方面。
到目前为止,系统模型应该处理引擎的启动。启动发动机意味着使用电气系统的电源并转动发动机并保持怠速。首先,制作一辆配备“Pumpe Duse E250”发动机和“BoschR10”电气系统的高尔夫“GT”。然而,未来可能会推出许多不同的系列,系统应该能够以最小的麻烦扩展功能和车型。
更新:根据建议编辑代码。
代码:
class Electrical
{
public:
virtual void OpenCircuit() = 0;
};
class Engine
{
public:
virtual void Crank(Electrical *) = 0;
};
class CarComponentFactory
{
public:
virtual Engine* CreateEngine()=0;
virtual Electrical* CreateElectrical()=0;
};
class PumpeDuseE250:public Engine
{
virtual void Crank(Electrical *pEle)
{
pEle->OpenCircuit();
//Do the crank
}
};
class BoschR10: public Electrical
{
virtual void OpenCircuit()
{
//Open the Circuit
}
};
class GTFactory:public CarComponentFactory
{
public:
virtual Engine* CreateEngine()
{
return new PumpeDuseE250();
}
virtual Electrical* CreateElectrical()
{
return new BoschR10();
}
};
class VWGolf
{
auto_ptr<Engine> mpEngine;
auto_ptr<Electrical> mpElectrical;
public:
VWGolf(Engine *pEngine, Electrical *pElectrical):mpEngine(pEngine), mpElectrical(pElectrical)
{
}
void Start()
{
mpEngine->Crank(mpElectrical.get());
}
~VWGolf()
{
}
};
创建对象的示例:
void main()
{
GTFactory Factory;
VWGolf golfGT(Factory.CreateEngine(), Factory.CreateElectrical());
golfGT.Start();
}
请注意,“BoschR10”等名称是虚构的。
I am trying to get the knack of good system design. As there is no hard and fast rules for good system design, I request you to give me some valuable suggestions. I prepared an imaginary system and prepared a design for that. Please let me know if you think if this design is good or bad. Is there a better way? I used Abstract Factory and Dependency Injection in the solution.
Problem:
Design a system which runs in cars and controls it. The requirement is to make a system for Volkswagen Golf controlling components like engine, electrical, gearbox etc.
There can be different versions of Golf like BlueMotion, Twist, GT etc which uses different components. For example if GT uses engine a and electrical b, BlueMotion can use engine c and electrical d.
The engines, electricals and other components in different cars could be similiar, or could be completely different. For example a "Flat 4 CRDI" Engine uses CRDI technology for fuel injection and have a certain behaviour to reduce vibration. A "PumpeDuse V6" engine uses PD injection and its own method for reducing vibration which is completely different from "Flat4 CRDI". The difference is not only in the handling the vibrations but also most of the aspects of Engine behaviour.
As of now, the system model should handle the starting of Engine. Starting the engine means use power from the electrical system and crank the engine and keep idling. To start with, make a Golf "GT" with "Pumpe Duse E250" Engine and "BoschR10" Electrical system. However, many different series could be launched in the future and system should be able to extend the functionality and car models with minimal trouble.
Update: Code edited as per suggestions.
Code:
class Electrical
{
public:
virtual void OpenCircuit() = 0;
};
class Engine
{
public:
virtual void Crank(Electrical *) = 0;
};
class CarComponentFactory
{
public:
virtual Engine* CreateEngine()=0;
virtual Electrical* CreateElectrical()=0;
};
class PumpeDuseE250:public Engine
{
virtual void Crank(Electrical *pEle)
{
pEle->OpenCircuit();
//Do the crank
}
};
class BoschR10: public Electrical
{
virtual void OpenCircuit()
{
//Open the Circuit
}
};
class GTFactory:public CarComponentFactory
{
public:
virtual Engine* CreateEngine()
{
return new PumpeDuseE250();
}
virtual Electrical* CreateElectrical()
{
return new BoschR10();
}
};
class VWGolf
{
auto_ptr<Engine> mpEngine;
auto_ptr<Electrical> mpElectrical;
public:
VWGolf(Engine *pEngine, Electrical *pElectrical):mpEngine(pEngine), mpElectrical(pElectrical)
{
}
void Start()
{
mpEngine->Crank(mpElectrical.get());
}
~VWGolf()
{
}
};
Example for Creating Objects:
void main()
{
GTFactory Factory;
VWGolf golfGT(Factory.CreateEngine(), Factory.CreateElectrical());
golfGT.Start();
}
Note that the names like "BoschR10" are imaginary.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这就是我要做的:
从基础开始:
然后根据需要进行扩展。
问题是,到目前为止,还没有进一步的要求。
没有要求汽车使用不同的组件来完成任何事情,所以我暂时将它们排除在外。我们只需要能够制造出具有特定组件的汽车即可。但在我们知道这些组件应该做什么之前,没有理由让它们比注释更多:
我们就这样了。这是我的设计。考虑到这个起点,当您提出更多需求时,只需很少的代码即可扩展系统。当需求发生变化时,几乎没有什么可以破坏或需要重写。它简单干净。
所以现在,我的结论是,这是符合您要求的理想设计。与大型继承层次结构相比,它更加健壮,更加高效,并且包含错误的可能性也小得多。而且它也是百分百可重用的代码。
正如您可能从我在您的问题下的评论中了解到的那样,好的设计取决于上下文。抽象意义上的“汽车”并没有什么好的设计。
但是我们可以为“赛车游戏的汽车”,或者“管理汽车工厂的系统”,或者“显示汽车的各种属性的状态(门打开或关闭,当前速度,燃料)提出好的设计。水平,未系安全带)。”
设计并不取决于您正在建模的对象,而是取决于您想用它们做什么。从你到目前为止的描述来看,这些物体的目的除了存在之外什么都没有。所以我建议一个模型,里面只有汽车,没有其他东西。
最好的设计不是最能描述某个物理对象的设计,而是最能允许您的应用程序执行其应该执行的操作的设计。
如果您的应用程序要做的就是“拥有一辆车”,那么模型就变得非常简单。
Here's what I'd do:
start with the basics:
and then extend on it as needed.
The thing is, so far, there are no further requirements.
There is no requirement that the car uses the different components for anything, so I leave them out for now. We just have to be able to create a car with specific components. But until we know what these components are supposed to do, there's no reason to make them anything more than a comment:
And there we are. This is my design. Given this starting point, it takes very little code to extend the system when you come up with more requirements. There is virtually nothing that can break, or require rewrites when the requirements change. It is simple and clean.
So for now, I conclude that this is the ideal design matching your requirements. It is far more robust, far more efficient and far less likely to contain bugs than your big inheritance hierarchy. And it is one hundred percent reusable code too.
As you've probably gathered from my comments under your question, good design depends on context. There is no good design for "cars" in the abstract.
But we can come up with good designs for "cars for a racing game", or "a system for managing a car factory", or for "displaying the status of various properties of a car (doors open or closed, current speed, fuel level, unfastened seatbelts)."
The design doesn't depend on what objects you're modelling, but on what you want to do with them. And from your description so far, the purpose of the objects is nothing, except to be there. So I suggest a model where the cars are there and nothing else.
The best design is not the one that best describes some physical object, but the one that best allows your application to do what it is supposed to do.
And if all your application is supposed to do is "have a car", then the model becomes pretty simple.
我会删除工厂(实际上应用依赖注入)和 VWGolf 之间的依赖关系,并且我会确保使用 RAII 而不是原始指针。你的 main 应该看起来更像这样:
计算一下你需要进行多少次模拟才能用你的方式与我的方式测试 VWGolf。
I'd remove the dependency between factories (actually apply dependency injection) and VWGolf and I'd make sure to be using RAII instead of raw pointers. You're main should look more like so:
Count how many mocks you'd have to make in order to test VWGolf with your way vs. mine.