我们知道,mixin是一种将某些行为“插入”另一个类的设计模式。例如,在Ruby中,我们可以编写这样的代码
module Flyable
def fly
puts "I'm flying";
end
end
class Bird
include Flyable
end
C ++不支持语言级别的混合功能,但是我们可以使用多重继承将代码插入派生类。但是此解决方案仍然存在其自身的问题,例如钻石继承,无法覆盖接口中的虚拟方法,无法访问“同级”模块中的成员等。
后来我发现我可以使用#include宏将代码段插入类定义中以实现混合:
// in Flyable.h
public:
void fly() {
// do something ...
}
float flySpeed;
在另一个文件中
// in Bird.h
class Bird {
#include "Flyable.h"
};
因此,所有成员变量和函数都将插入到Bird类。
此解决方案似乎没有明显的缺点。代码可以很好地组织到不同的文件中。精心设计的模块不会重叠功能/规则,可以避免可能的名称冲突。
但是我仍然担心有些问题我还没有看到。这种混入有什么问题吗?
修改
我知道在类定义中使用#include很奇怪。但这确实解决了问题。如果项目中确实使用了它,那么程序员可能会习惯它。所以我想知道是否有实践原因,我们应该避免这样的代码?不仅丑陋或怪异,或者没有人这样写。
修改
我忘了解释代码的目的了。简而言之,这是在没有虚拟继承的情况下解决钻石问题的方法。据我了解,钻石问题的根本原因是滥用OOP。鸟“ is-a” FlyableAnimal,FlyableAnimal“ is-a”动物,鸟“ is-a”食肉动物,食肉动物“ is-a”动物。这种滥用的实际意图是代码重用,因此“ -able”比“ is-a”关系更好。和mixin可以带来“ -able”的关系。
在C ++中,mixin可以通过多重继承来实现。这是相当不错的,但是它将禁用多态。例如,我们具有带有纯虚函数的Animal接口,并且我们希望Bird实现它们。然后,我们不能使用多重继承将实现注入到Bird类。还有其他一些技术可以实现这一点,例如合成。但是我还没有看到像真正的mixin一样简单的解决方案。这就是我想到混入#include的原因。
答案 0 :(得分:2)
此解决方案似乎没有明显的缺点。
缺点:
devs在成为该项目的有效开发人员之前,需要学习更多的知识。仅当您必须在其他项目中维护其他人的非标准hack一段时间后,这才显而易见。
这是意外的;这会引入错误,导致开发上的大量延迟(“嘿,我写了这个程序,但是没有编译。” /“是的,在那些文件中有一个奇怪的包含内容,您需要编写这样的代码代替”)。
代码可以很好地组织到不同的文件中。
整个行业都采用以下惯例来组织代码:
域中的所有内容,单个API中的所有内容(无论这意味着单个类还是一组免费函数的集合),并且通常最多包含两个文件:一个用于声明,一个用于定义。
在可行的情况下,仅保留标题为头
在不可能的情况下,请在.impl或_impl类中编写实现,并将其包含在标头中(这不是实际标准,但是您看到它与模板代码一起使用)。
您的解决方案与此截然不同(也就是说,组织得不好,没有很好地组织到不同的文件中)。
精心设计的模块可以避免名称冲突,而不会重叠功能/规则。
当人们做出这种区分时(“是的,代码是可维护的,您只需要加倍小心”),他们就会想象您正在查看问题,应用解决方案,提交并解决了问题。
这样做时您容易忘记的部分:
但是我仍然担心有些问题我还没有看到。这种混入有什么问题吗?
总结(tldr):
在整个项目过程中,您将不断增加维护工作量。
这对于任何新员工来说都是违反直觉的,并增加了其他人加入您的项目的成本。
任何遵循自己的直觉/所获得的经验的开发人员都希望在您的项目中看到其他内容(代码中WTF / sloc的增加)。
这将在将来引起您团队的摩擦;至少如果您的团队中的开发人员关心代码的质量和易用性,那么至少会如此。