接口继承中一个常见问题的思考

本文探讨了DirectUI界面库设计中的接口继承问题,对比分析了接口与实现交叉继承、基于模板的实现及多重继承三种解决方案,讨论了各自的优缺点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

以前在设计DirectUI界面库(该界面库现已开源, 可到  这里 下载)架构时,遇到一个接口继承相关的问题,当时没有太好的解决方案,却一直个耿耿于怀, 现在重新思考整理下。

我们的DirectUI控件层次大概如下: 

其中, 类名以 I 开头的都是接口:
IObject表示框架的基本接口, 要求实现类似COM里IUnknown的功能,
IControl表示控件的基本接口, 所有控件都从该接口继承,
IControlContainer表示容器类控件的基本接口,
IButton表示Button类的基本接口,
IPanel表示某种容器控件接口。
 
当然上面的框架是简化的情况,实际情况比上面的复杂的多, 但该图已经可以帮我们说明这里的情况。

在真正实现Panel和Button时,我们会发现大量的代码是重复和可以共用的,因此在实际实现时, 我们的框架可能会变成这样:

也就是说我们会出现接口和实现交叉继承的情况,实际上我自己在实现时就是用这种方法的, 我想大部分人都会用这种方法(实际上WPF也是用这种方法的)。
这种方法的缺点是显而易见的, 接口中包含了实现,基本上让接口失去了它应有的作用, 这在组件式编程中是致命的,比如本来在C++中我可以封装成DLL,然后以类似COM的方式暴露接口给外部, 现在用这种方式却没法做到了(只能用导出类的方式)。

那么我们怎样才能既基于接口编程, 又能在实现时实现代码重用呢? 这个东西实际上是个语法糖, 即如何既符合C++语法又能实现我们这个需求。

于是,我们想到了如下的实现方式: 

我们的这种实现方式基于C++模板, 总的来说就是把我们要实现的接口通过模板参数传到继承类体系的最底层, 该方式的代码大概如下:
class IObject
{
};

class IControl:  public IObject
{
};

class IButton:  public IControl 
{
};

template<typename TBase>
class CObjectImpl:  public TBase
{
};

template<typename TBase>
class CControlImpl:  public TBase
{
};

template<typename T, typename TBase>
class CButtonImpl:  public TBase
{
};

class CButton:  public CButtonImpl<CButton, IButton>
{
};

该方式基本上完全满足我们上面的需求,既实现了代码重用,又是基于接口编程,但是你有没有发现它有一个致命的缺点, 这个缺点就是C++模板导致的代码膨胀, 我们在  C++模板会使代码膨胀吗 对模板导致的代码膨胀有相关分析。也就是说我们上面的设计会导致每种控件继承类都有一份重复的代码, 即 CControlImpl<IButton>和CControlImpl<IPanel>因为是不同的类实例, 因此它们会生成2分代码。你可能会觉得这个不算什么, 但是想想控件的继承类可能有好几十甚至上百,最终的可执行文件会被撑大不少。

那么有没有其他的方法来实现呢?  既能基于接口编程, 又能实现代码重用,还没有代码膨胀的问题。

于是,我们想到了下面这种实现方式:




这种方式是最原始的方式, 实际上就是把接口体系单独独立出来, 把实现体系也单独独立出来,  然后在最终类(Button和Panel)里继承组合起来。 当然这种方式也有缺点, 就是我们要多做些工作,因为我们要在最终类(Button)里实现接口(IButton), 在实现时我们要把所有接口需要实现的方法转发给实现类(CButtonImpl)。

最后,总结下上面三种方法:
第一种实现和接口混合继承的方法最简单,也最容易理解, 缺点是没法完全基于接口编程; 第二种基于模板的方法比较难理解,实现上也比较简单, 缺点是代码膨胀; 第三种多重继承的方法也比较容易理解, 缺点是我们要多做一些工作。

我暂时就想到这些方法, 不知道其他朋友对上面的问题一般是怎么解决的, 有什么好的解决方法?

转载于:https://www.cnblogs.com/weiym/archive/2013/02/08/2909387.html

### Python 继承相关的常见题型和练习 #### 一、单继承与多态 在单继承的情况下,子类可以重写父类的方法来实现不同的行为。这种特性称为 **多态**。 ```python class Animal: def speak(self): print("动物发出声音") class Dog(Animal): def speak(self): print("汪汪") # 子类重写了父类方法 dog = Dog() dog.speak() # 输出: 汪汪 ``` 此类型的题目通常考察学生对 `super()` 函数的理解以及如何通过继承扩展功能[^1]。 --- #### 二、多重继承与方法解析顺序 (MRO) 当一个类从多个基类派生时,Python 使用 C3 线性化算法决定方法调用的优先级顺序。这被称为 **方法解析顺序 (Method Resolution Order)**。 ```python class A: def method(self): print("A's method") class B(A): pass class C(A): def method(self): print("C's method") class D(B, C): pass obj_d = D() obj_d.method() # 输出: C's method print(D.mro()) # 查看D类的方法解析顺序 ``` 这类题目常用于测试考生对于复杂继承结构下方法查找机制的认识程度[^3]。 --- #### 三、`super()` 的应用 `super()` 是一种方便的方式让开发者能够访问当前实例所关联的父类或者兄弟类中的成员变量或方法。 ```python class ParentClass: def __init__(self, value): self.value = value def get_value(self): return self.value class ChildClass(ParentClass): def __init__(self, value, extra): super().__init__(value) # 调用了ParentClass.__init__ self.extra = extra child_instance = ChildClass(10, "extra info") print(child_instance.get_value(), child_instance.extra) # 输出: 10 extra info ``` 该种类型的问题主要评估学习者能否正确运用 `super()` 实现更灵活的设计模式[^5]。 --- #### 四、抽象基类与接口设计 利用模块 abc 中的功能定义抽象基类可以帮助强制执行某些特定的行为约束条件。 ```python from abc import ABC, abstractmethod class AbstractBaseClass(ABC): @abstractmethod def required_method(self): raise NotImplementedError() class ConcreteImplementation(AbstractBaseClass): def required_method(self): print("实现了必需的方法") try: instance_of_abstract = AbstractBaseClass() # 尝试实例化会抛出异常 except TypeError as e: print(e) concrete_obj = ConcreteImplementation() concrete_obj.required_method() # 正确输出消息 ``` 此类问题旨在检验应试者是否懂得借助抽象基类构建更加健壮的应用程序框架[^4]。 --- #### 五、组合 vs. 继承的选择依据 有时候并非一定要采用继承关系解决问题,而是可以通过对象之间的协作完成任务——即所谓的 “组合优于继承”。 ```python class Wing: def flap(self): print("翅膀拍动") class Bird: def __init__(self): self.wing = Wing() # 创建Wing的一个实例作为Bird的一部分 birdie = Bird() birdie.wing.flap() # 结果显示鸟儿拥有翅膀并能扇动它 ``` 这部分内容往往用来引导思考什么时候应该偏好使用组合而非传统意义上的继承方式[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值