多继承与Mixin比较

参考《c++ primer》

参考《松本行弘的程序世界》

  • 面向对象有抽象封装,继承,多态三大特性
  • 抽象封装通过private,public等控制数据和方法的访问,并将实现和调用分割

继承(c++为例)

数据和普通方法继承情况
class Base {
public:
  string name;
  Base() { name = "base class"; }
  void print() { cout << this->name << endl; }
};

class Sub : public Base {
public:
  string name;
  Sub() : Base() { name = "sub class"; }
  void print() {
    cout << name << endl;
    cout << Base::name << endl;
    Base::print();
  }
};

int main() {
  Sub obj;
  cout << obj.Base::name << endl;
  obj.Base::print();
  cout << obj.name << endl;
  obj.print();

  return 0;
}
// 输出
//base class
//base class
//sub class
//sub class
//base class
//base class
  • 继承关系中,派生类会直接复制一个基类的数据样本,即使字段相同
  • 继承关系中,基类普通方法,直接继承,编译时确定
  • 继承关系中,基类虚方法virtual,根据具体派生类覆盖override基类实现,动态绑定
  • 基于编译时确定,基类方法无法通过this等访问派生类数据字段和方法
  • 基于编译时确定,派生类访问基类数据字段和方法,要明确指出
  • Sub继承了Base的print,Base::print在编译时已经确定访问的是Base的name,无法访问Sub的name
  • public Base说明了Sub继承的print, name是否可以通过实例在外部访问,换成private编译错误
  • Sub中包含重复数据name和方法print时,如果调用基类要用Base::明确指出
多继承
  • 多继承的痛点,类关系复杂,优先顺序模糊,多个父类包含同一方法时冲突
  • c++解决多继承的问题,要明确指定父类
java单继承多接口
  • 实现继承用extends,规格继承用implements,规格接口的实现一般基于组合委托给实现该接口的实例

Mixin

  • c++多继承父类有相同名称时要明确指定使用父类,java为了实现多继承采用了接口 正真的实现要通过组合委托,某些情况下可以使用Mixin编程技巧
  • Mixin类应该是抽象单一通用的,用于扩展继承者的功能,自身实例化无意义
  • python系统级例子ThreadingTCPServer,ThreadingMixIn,ForkingMixIn,TCPServer,UDPServer
# 以序列化字符串为场景,不好的地方,学生和序列化关联不明显

class JsonMixin(object):
    """只考虑int, str, dict, class
    """

    def to_json(self):

        def help(obj):
            result = []
            for k, v in obj.items():
                if isinstance(v, str):
                    result.append(f'"{k}":"{v}"')
                elif isinstance(v, int):
                    result.append(f'"{k}":{str(v)}')
                elif isinstance(v, dict):
                    result.append(f'"{k}":{help(v)}')
                elif isinstance(v, object):
                    result.append(f'"{k}":{help(v.__dict__)}')
            return "{" + ",".join(result) + "}"

        return help(self.__dict__)


class XmlMixin(object):
    """只考虑int, str, dict, class
    """

    def to_xml(self):

        def help(tag, obj):
            result = f"<{tag}>"
            for k, v in obj.items():
                if isinstance(v, str):
                    result += f'<{k}>"{v}"</{k}>'
                elif isinstance(v, int):
                    result += f'<{k}>{v}</{k}>'
                elif isinstance(v, dict):
                    result += help(k, v)
                elif isinstance(v, object):
                    result += help(k, v.__dict__)
            result += f"</{tag}>"
            return result

        return help(self.__class__.__name__, self.__dict__)


class Attr(object):

    def __init__(self, addr, phone):
        self.addr = addr
        self.phone = phone


class Student(JsonMixin, XmlMixin):

    def __init__(self, name, age, score, attr):
        self.name = name
        self.age = age
        self.score = score
        self.attr = attr


swk = Student("孙悟空", 500, {"qi_shi_er_bian": 100, "huo_yan_jin_jing": 95}, Attr("东胜神洲傲来国花果山", "叫你一声爷爷"))
print(swk.to_json())
print(swk.to_xml())
  • 不管上面的例子还是之前说的系统级例子,Mixin要通用抽象,首先不能包含太多的数据字段,但功能和数据无关的场景很少,应该说Mixin是扩展符合一定结构的类上
  • 如果是继承扩展结构,或方法和数据强依赖,传统的继承更合适
  • 由于动态语言传递了self,this等当前实例的指针,很容易将Mixin功能和数据分开
比较
  • 基于c++多继承,上面的功能如何实现那,每个父类负责将自己的数据序列化为字符串
#include <iostream>
#include <string>
#include <vector>
using namespace std;

class People {
public:
  string name;
  int age;
  People(string name, int age) : name(name), age(age) {}
  string to_json() {
    return "{\"name\":\"" + name + "\",\"age\":" + to_string(age) + "}";
  }
};

class Attr {
public:
  string addr;
  string phone;
  Attr(string addr, string phone) : addr(addr), phone(phone) {}
  string to_json() {
    return "{\"addr\":\"" + addr + "\",\"phone\":\"" + phone + "\"}";
  }
};

class Score {
public:
  string title;
  float score;
  Score(string title, float score) : title(title), score(score) {}
  string to_json() {
    return "{\"title\":\"" + title + "\",\"score\":" + to_string(score) + "}";
  }
};

class Student : public People, public Attr, public vector<Score> {
public:
  Student(string name, int age, string addr, string phone)
      : People(name, age), Attr(addr, phone) {}
  void add_score(Score &s) { vector<Score>::emplace_back(s); }
  string to_json() {
    string result = "{\"people\":" + People::to_json() +
                    ",\"attr\":" + Attr::to_json() + ",\"score\":[";
    for (auto s = vector<Score>::begin(); s != vector<Score>::end(); ++s) {
      result += s->to_json() + ",";
    }
    result = result.substr(0, result.size() - 1);
    result += "]}";
    return result;
  }
};

int main() {
  Student stu("孙悟空", 500, "东胜神洲傲来国花果山", "叫你一声爷爷");
  Score boss1("七十二变", 100.0);
  Score boss2("火眼金睛", 95.5);
  stu.add_score(boss1);
  stu.add_score(boss2);
  cout << stu.to_json() << endl;
  return 0;
}
// 输出
// {"people":{"name":"孙悟空","age":500},"attr":{"addr":"东胜神洲傲来国花果山","phone":"叫你一声爷爷"},"score":[{"title":"七十二变","score":100.000000},{"title":"火眼金睛","score":95.500000}]}
  • 如果RTTI支持运行时获取类数据字段,Mixin风格伪代码如下
#include <iostream>
#include <string>
#include <vector>
#include <typeinfo>
using namespace std;

class JsonMixin {
public:
  string to_json(void *obj) {
    // 编程语言的RTTI或reflection机制如果支持获取到类型以及类字段,这里就可以实现
    return "";
  }
};

class People {
public:
  string name;
  int age;
  People(string name, int age) : name(name), age(age) {}
};

class Attr {
public:
  string addr;
  string phone;
  Attr(string addr, string phone) : addr(addr), phone(phone) {}
};

class Score {
public:
  string title;
  float score;
  Score(string title, float score) : title(title), score(score) {}
};

class Student : public People,
                public Attr,
                public vector<Score>,
                public JsonMixin {
public:
  Student(string name, int age, string addr, string phone)
      : People(name, age), Attr(addr, phone) {}
  void add_score(Score &s) { vector<Score>::emplace_back(s); }
  string to_json() {
    // return typeid(*this).name();
    return JsonMixin::to_json(this);
  }
};

int main() {
  Student stu("孙悟空", 500, "东胜神洲傲来国花果山", "叫你一声爷爷");
  Score boss1("七十二变", 100.0);
  Score boss2("火眼金睛", 95.5);
  stu.add_score(boss1);
  stu.add_score(boss2);
  cout << stu.to_json() << endl;
  return 0;
}

补充Mixin维基解释

https://encyclopedia.thefreedictionary.com/mixin

  • 在面向对象的编程语言中,Mixin是一个包含供其他类使用的方法的类,而不必是这些其他类的父类,这些其他类如何访问Mixin的方法取决于语言
  • Mixin鼓励代码重用,可用于避免多重继承可能导致的继承歧义(钻石问题),或解决语言中对多重继承缺乏支持的问题
  • Mixin是一种语言概念,并不一定是语法特性,允许程序员将一些代码注入到类中。 Mixin编程是一种软件开发风格,其中功能单元在一个类中创建,然后与其他类混合
  • Mixin类充当父类,包含所需的功能。然后子类可以继承或简单地重用此功能,但不能作为专门化的手段。Mixin会将所需的功能导出到子类,而不会创建严格的、单一的“是一个”关系,这是Mixin和继承概念的重要区别
  • 好处
    • 它提供了一种多重继承机制,允许一个类使用来自多个类的通用功能,但没有多重继承的复杂语义
    • 代码可重用性,当程序员想要在不同的类之间共享功能时,Mixin 很有用。无需一遍又一遍地重复相同的代码,可以简单地将通用功能分组到一个 mixin 中,然后将其包含到每个需要它的类中
    • Mixins 只允许继承和使用来自父类的所需特性,而不一定是来自父类的所有特性
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值