多重继承

继承是面向对象编程的一个重要的方式,通过继承,子类就可以扩展父类的功能

Animal类层次设计

4 种动物:Dog(狗)、Bat(蝙蝠)、Parrot(鹦鹉)、Ostrich(鸵鸟)

哺乳动物鸟类归类,类层次:

1

能跑能飞 归类,类层次

2

要把上面的两种分类都包含进来,就得设计更多的层次

哺乳类:能跑的哺乳类,能飞的哺乳类

鸟类:能跑的鸟类,能飞的鸟类

这么一来,类的层次就复杂了:

3

如果要再增加 “宠物类” 和 “非宠物类”,这么搞下去,类数量指数增长(类爆炸)

python中多重继承的用法

主要类层次仍按照哺乳类和鸟类设计:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Animal(object):
    pass

# 大类:
class Mammal(Animal):
    pass

class Bird(Animal):
    pass

# 各种动物:
class Dog(Mammal):
    pass

class Bat(Mammal):
    pass

class Parrot(Bird):
    pass

class Ostrich(Bird):
    pass

给动物加上Runnable和Flyable功能,只需要先定义好Runnable和Flyable的类:

1
2
3
4
5
6
7
class Runnable(object):
    def run(self):
        print('Running...')

class Flyable(object):
    def fly(self):
        print('Flying...')

需要Runnable功能的动物,就多继承一个Runnable

1
2
class Dog(Mammal, Runnable):
    pass

需要Flyable功能的动物,就多继承一个Flyable

1
2
class Bat(Mammal, Flyable):
    pass

通过多重继承,一个子类就可以同时获得多个父类的所有功能


Mixin

设计类的继承关系时,通常,主线都是单一继承下来的(Ostrich继承自Bird)

但是,如果需要 混入 额外的功能,通过多重继承就可以实现(让Ostrich除了继承自Bird外,再同时继承Runnable)。这种设计通常称之为 Mixin

为了更好地看出继承关系,把Runnable和Flyable改为RunnableMixin和FlyableMixin

类似还可以定义出肉食动物CarnivorousMixin和植食动物HerbivoresMixin,让某个动物同时拥有好几个 Mixin:

1
2
class Dog(Mammal, RunnableMixin, CarnivorousMixin):
    pass

Mixin 的目的就是给一个类增加多个功能

这样,在设计类的时候,优先考虑通过多重继承来组合多个 Mixin 的功能,而不是设计多层次的复杂的继承关系

python中通过多重继承来组成只是一种实现手段,go里一般用嵌入类型的做法

Python 自带的很多库也使用了 Mixin

举例

Python TCPServer和UDPServer,而要同时服务多个用户就必须使用多进程或多线程模型,这两种模型由ForkingMixin和ThreadingMixin提供 通过组合,就可以创造出合适的服务来

比如,编写一个多进程模式的 TCP 服务,定义如下:

1
2
class MyTCPServer(TCPServer, ForkingMixin):
    pass

编写一个多线程模式的 UDP 服务,定义如下:

1
2
class MyUDPServer(UDPServer, ThreadingMixin):
    pass

如果打算搞一个更先进的协程模型,可以编写一个CoroutineMixin:

1
2
class MyTCPServer(TCPServer, CoroutineMixin):
    pass

这样一来,不需要复杂而庞大的继承链,只要选择组合不同的类的功能,就可以快速构造出所需的子类

小结

由于 Python 允许使用多重继承,因此,Mixin 就是一种常见的设计

只允许单一继承的语言(如 Java)不能使用 Mixin 的设计

Python 经典类和新式类的区别

http://blog.csdn.net/oxiaoxio/article/details/50463484

首先: Python 2.x 中默认都是经典类,只有显式继承了 object 才是新式类 Python 3.x 中默认都是新式类,不必显式的继承 object

动物的例子就相当于 Java 的单继承 + 接口机制

TCP 服务的例子 Java 或 C# 可以用 “组合” 实现


Mixin 为了解决多重继承的问题

《松本行弘的程序世界》列举了以下三点:

  • 结构复杂化

单一继承,类的父类是什么,父类的父类是什么,都很明确

多重继承,一个类有多个父类,这些父类又有自己的父类,类之间的关系就很复杂了

  • 优先顺序模糊

A,C 类同时继承了基类,B 类继承了 A 类,然后 D 类又同时继承了 B 和 C 类

所以 D 类继承父类的方法的顺序应该是 D、B、A、C 还是 D、B、C、A,或者是其他的顺序,很不明确

  • 功能冲突

多重继承有多个父类,不同的父类中有相同的方法是就会产生冲突

如果 B 类和 C 类同时又有相同的方法时,D 继承的是哪个方法就不明确了,因为存在两种可能性


有些语言解决了这个问题,但是并不是所有语言都想要去纠结这个问题

为利用多继承的优点又解决多继承的问题,提出规格继承和实现继承

规格继承指一堆方法名的集合,而实现继承除了方法名还允许有方法的实现

  • Java

    规格继承,叫 interface(Java8 已有默认方法)

  • Ruby

    实现继承,也可以叫 Mixin

  • Ruby

    module

某种程度上来说,继承强调 I am,Mixin 强调 I can

当你 implement 了这个接口或者 include 这个 module 的时候,然后就你行你上

所以这又可以扯到 duck typing 去了

具体看书


Mixin 实质上是利用语言特性(比如 Ruby 的 include 语法、Python 的多重继承)来更简洁地实现组合模式

Java 为例,实现一个可复用的 “打标签” 组件(Taggable),并且应用到帖子(Post)模型上:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import java.util.List;
import java.util.ArrayList;

interface Entity {
    public int getId();
    public int getKind();
}

interface Taggable {
    public void addTag(int tagId);
    public List<Integer> getTags();
}

class TaggableImpl implements Taggable {
    private Entity target;

    public TaggableImpl(Entity target) {
        this.target = target;
    }

    public void addTag(int tagId) {
        int id = target.getId();
        int kind = target.getKind();
        System.out.println("insert into ... values "
                + id + ", "
                + kind + ", "
                + tagId + ")");
    }

    public ArrayList<Integer> getTags() {
        // query from database
        return new ArrayList<Integer>();
    }
}

class Post implements Entity, Taggable {
    public final static int KIND = 1001;

    private Taggable taggable;
    private int id;
    private String title;

    public Post(int id, String title) {
        this.id = id;
        this.title:  title;
        this.taggable = new TaggableImpl(this);
    }

    public int getId() {
        return id;
    }

    public int getKind() {
        return KIND;
    }

    public void addTag(int tagId) {
        taggable.addTag(tagId);  // delegate
    }

    public ArrayList<Integer> getTags() {
        return taggable.getTags();  // delegate
    }
}

这里使用组合模式,在 TaggableImpl 中实现打标签的逻辑,然后让 Post 类和 TaggableImpl 类都实现 Taggable 接口

Post 类中创建一个 TaggableImpl 实例并在实现 Taggable 时将相应方法调用委托过去

Python 允许多重继承,所以 “委托方法” 的过程可以简化为直接将实现混入宿主类中

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class TagMixin(object):
 
    def add_tag(self, tag_id):
        sql = ('insert into target_tagged'
               ' (target_id, target_kind, tag_id, creation_time) '
               'values (?, ?, ?, CURRENT_TIMESTAMP)')
        params = (self.ident, self.kind, tag_id)
        storage.execute(sql, params)
        storage.commit()
 
    def get_tags(self):
        sql = ('select tag_id, creation_time from target_tagged '
               'where target_id = ? and target_kind = ?')
        params = (self.ident, self.kind)
        cursor = storage.execute(sql, params)
        return cursor.fetchall()
 
 
class Post(Model, TagMixin):

    kind = 1001
 
    def __init__(self, ident, title):
        self.ident = ident
        self.title:  title
 
    def __repr__(self):
        return 'Post(%r, %r)' % (self.ident, self.title)

这里多重继承的用法是非常谨慎的:

TagMixin 类是单一职责的 TagMixin 类对宿主类(Post)一无所知,除了要求宿主类有 ident 和 kind 这两个属性(等价于 Java 中要求宿主类实现 Entity 接口)

宿主类的主体逻辑不会因为去掉 TagMixin 而受到影响,同时也不存在超类方法调用(super)以避免引入 MRO 查找顺序问题所以这样比 Java 中的组合模式实现方式更加简洁

同时因为使用得当,钻石调用、MRO 查找顺序等多重继承的弊病也没有被踩到

当然,这种 Duck Type 的设计也比显式的接口约束对开发者有更高的要求,要求代码中无 interface 而开发者脑海中有清晰的 interface

Ruby 的 include 语法实现的 Mixin 也同理

mixin 不是多继承,mixin 是 duck type 的语法糖,让你可以不用去把一坨坨 Interface 继承一遍然后才能彼此调用接口

mixin 是一种特殊的多重继承,也就是多重继承的子集

使用 Mixin 的好处是,同时享有单一继承的单纯性和多重继承的共有性

作为 Mixin 类,需要满足以下条件:

  • 不能单独生成实例对象,属于抽象类
  • 不能继承 Mixin 以外的类