接口是对动作的抽象,抽象类是对根源的抽象。
抽象类表示的是,这个对象是什么。接口表示的是,这个对象能做什么。比如,男人,女人,这两个类(如果是类的话……),他们的抽象类是人。说明,他们都是人。
人可以吃东西,狗也可以吃东西,你可以把“吃东西”定义成一个接口,然后让这些类去实现它.
抽象类表示的是,这个对象是什么。接口表示的是,这个对象能做什么。比如,男人,女人,这两个类(如果是类的话……),他们的抽象类是人。说明,他们都是人。
人可以吃东西,狗也可以吃东西,你可以把“吃东西”定义成一个接口,然后让这些类去实现它.
Mixin 实质上是利用语言特性(比如 Ruby 的 include 语法、Python 的多重继承)来更简洁地实现组合模式。
以如下 Java 伪码为例,实现一个可复用的“打标签”组件(Taggable),并且应用到帖子(Post)模型上: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 中应该怎么做呢?因为 Python 允许多重继承,所以“委托方法”的过程可以简化为直接将实现混入宿主类中: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 也同理。