[英]Design interfaces
I have what I believe is a fairly common problem. 我有一个我认为是相当普遍的问题。 There is an interface called
IService
, and some derived classes that implements this interface; 有一个称为
IService
的接口,还有一些实现此接口的派生类。 ServiceA, ServiceB, ServiceC and ServiceD. ServiceA,ServiceB,ServiceC和ServiceD。
ServiceA and ServiceB needs a function called getSomeType()
to be exposed, but this function is not needed in the other derived classes. ServiceA和ServiceB需要
getSomeType()
一个名为getSomeType()
的函数,但在其他派生类中则不需要此函数。 Also ServiceD needs another function exposed that the other derived classes doesn't need. 另外,ServiceD还需要其他公开类不需要的另一个函数。 How should I solve this problem?
我应该如何解决这个问题? I feel that using
dynamic_cast
is not the right way to do it, or is it? 我觉得使用
dynamic_cast
不是正确的方法,是吗? I also considered creating a new interface so that ServiceA and ServiceB implements two interfaces. 我还考虑过创建一个新接口,以便ServiceA和ServiceB实现两个接口。
class IService
{
public:
virtual IService() {};
virtual void start() = 0;
virtual void stop() = 0;
};
class ServiceA : public IService
{
public:
void start() override;
void stop() override;
ISomeType * getSomeType();
};
Derive IServiceAB from IService, derive ServiceA and ServiceB from IServiceAB, and derive ServiceD from IService. 从IService派生IServiceAB,从IServiceAB派生ServiceA和ServiceB,并从IService派生ServiceD。
One other way is too have a look into the decorator pattern (design patterns). 另一种方法是查看装饰器模式(设计模式)。
There most important tool in your design arsenal is a so-called 'substitution principle'. 设计库中最重要的工具是所谓的“替代原理”。 When you use public inheritance with polymorphism (btw, in C++ world we usually do not say 'implements interface', since interface is not a C++ term) you are claiming so-called IS-A principle - that is, you claim that for the outside observer ServiceA is, for all intents and purposes, IService.
当您将公共继承与多态性一起使用时(顺便说一句,在C ++的世界中,我们通常不说“实现接口”,因为接口不是C ++术语),您会声称是所谓的IS-A原理-也就是说,您声称出于所有意图和目的,外部观察者ServiceA都是IService。 No more, no less.
不多不少。 Everything which exists in IService exists in ServiceA.
IService中存在的所有内容都存在于ServiceA中。 Everything (visible outside) which exists in ServiceA, exists in IService.
ServiceA中存在的所有内容(外部可见)都在IService中存在。
Whenever you feel the need to do a dynamic_cast
, you are clearly violating this principle - because it means that IService is no longer ServiceA, you need to specifically cast to it. 每当您觉得需要进行
dynamic_cast
,您就明显违反了该原理-因为这意味着IService不再是ServiceA,因此需要专门将其强制转换。
Solution. 解。 Do not use public inheritance with polymorphism for classess which do not follow IS-A principle.
对于不遵循IS-A原理的类,请勿使用具有多态性的公共继承。 In your case, ServiceA is not IService, which means, IService is not a suitable base class for ServiceA.
在您的情况下,ServiceA 不是 IService,这意味着IService不是ServiceA的合适基类。
This is often a tricky problem. 这通常是一个棘手的问题。 If, instead of service, we say we deal with animals - because we can use that as a proxy for "things that can do some things, but not other things".
如果说不是处理服务,而是要与动物打交道-因为我们可以将其用作“可以做某些事情但不能做其他事情的事情”的代理。 Say we have the animals
Fish
, Bird
, Cow
, Dog
and Cat
. 假设我们有动物
Fish
, Bird
, Cow
, Dog
和Cat
。
So, we think of things animals in general can do: 因此,我们认为动物通常可以做到:
Swim
, Fly
, Walk
Swim
, Fly
, Walk
Talk
Talk
So, we have for example Fish
, which, obviously, can swim
, but can't talk
, which a Dog
or Bird
can do. 因此,例如,我们有一条
Fish
,显然,它可以swim
,但不会talk
,而Dog
或Bird
可以做到。 A Bird
can fly but can't Swim
[yes, there are excellent swimming birds, I'm simplifying reality]. Bird
会飞,但不会Swim
[是的,有优秀的游泳鸟,我是在简化现实]。 A Dog
can make sounds, but certainly can't fly. Dog
可以发出声音,但肯定不会飞。 A Cat
can make sounds, swim (reluctantly), but won't be able to Fetch
. Cat
可以发出声音,游泳((勉强地)游泳),但无法Fetch
声音。
So, it becomes a problem of how we represent this ability to do certain things, but not other things. 因此,这成为我们如何代表这种能力去做某些事情而不是其他事情的问题。
There are several solutions, but ultimately, there are two possible solutions 有几种解决方案,但最终有两种可能的解决方案
Talk
on Fish
, or Swim
on Bird
. Talk
的Fish
,或Swim
的Bird
。 CanSwim
function, that answers whether the animal can swim or not. CanSwim
函数,用于回答动物是否会游泳。 In which case the Swim
function may throw an exception if it's still being called. Swim
函数仍在被调用,则它可能会引发异常。 Ultimately, somewhere, we need to know that they are different types of animals, and that some can do some things, others can't. 最终,在某个地方,我们需要知道它们是不同类型的动物,有些可以做某些事情,有些则不能。 Either asking a bird to swim will fail (and shouldn't happen) or will "do nothing".
要求一只鸟游泳会失败(并且不应该发生),或者“什么也不做”。 You really have to determine what the right thing to do is.
您确实必须确定正确的做法是什么。
I do have this problem in my compiler project. 我的编译器项目中确实有此问题。 I have an "abstract syntax tree" that represents the source code in a parsed form.
我有一个“抽象语法树”,以解析的形式表示源代码。 So it has nodes for variable declarations, function declaratins, assignments, binary operations, while-loops, for-loops, etc, etc. These have the same basic interface, which provides a
CodeGen
function that actually makes the (intermediate representation) - so a binary expression of a + b
will generate code to load a
, load b
and then add the two together. 因此,它具有用于变量声明,函数声明,赋值,二进制操作,while循环,for循环等的节点。这些节点具有相同的基本接口,该接口提供了一个
CodeGen
函数,该函数实际上使(中间表示)成为可能。 a a + b
的二进制表达式将生成代码以加载a
,加载b
,然后将两者加在一起。 Codegen for a function will call codegen for the body of the function, etc. 一个函数的Codegen将调用该函数的主体的codegen等。
There are half a dozen other general purpose functions that apply to (almost) all AST entries. 还有六种其他通用功能适用于(几乎)所有AST条目。 But there are a few operations that only really make sense on a particular situation, for example when dealing with binary operators, strings are not supported by the intermediate representation as
add
for concatenation. 但是,有一些操作仅在特定情况下才真正有意义,例如,在处理二进制运算符时,中间表示形式不支持将字符串作为连接的
add
。 So can't just load, load and add - have to call the strcat
function. 因此,不能只加载,加载和添加-必须调用
strcat
函数。 For these special cases, I use dynamic_cast
to check if the, in this case, the type is matching the string type, and if so, fall into the "special string operation" code-path. 对于这些特殊情况,我使用
dynamic_cast
来检查类型(在这种情况下)是否与字符串类型匹配,如果匹配,则属于“特殊字符串操作”代码路径。 I could add a isString
function for ALL of the types, but it would be rather awkward and not much benefit, as MOST of the time, it's not important. 我可以为所有类型添加一个
isString
函数,但这会很尴尬并且没有太多好处,因为在大多数情况下,这并不重要。 I don't think this is a great solution. 我认为这不是一个很好的解决方案。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.