设计原则:请重新审视“多重继承”,找机会拥抱一下“掺入(Mixin)”
名称解释
多重继承:我没有使用多重继承的经验,因此这里不多说,大学学的C++,可惜没有学好。
Mixin:一个Mixin是一个方法和属性的集合,不同的语言提供的实现机制不一样。类型定义的时候可以声明他想包含的Mixin(可以是多个),这些Mixin包含的方法会成为类型的一部分。
使用动机
代码复用 AND 运行时不改变。
Mixin是推论,MixinTarget是定理。如:C#的IEnumerable(MixinTarget)只包含一个方法,根据这个方法(定理)Enumerable(Mixin)扩展了N个方法(推论)。
示例(ExtJs4.2)
1 /// <reference path="../ext-all-debug-w-comments.js" /> 2 Ext.define('Enjoyable', { 3 play: function () { 4 console.log(this.getName() + '-play'); 5 } 6 }); 7 8 Ext.define('Workable', { 9 work: function () { 10 console.log(this.getName() + '-work'); 11 } 12 }); 13 14 Ext.define('User', { 15 mixins: { 16 'enjoyable': 'Enjoyable', 17 'workable': 'Workable' 18 }, 19 config: { name: 'unknow' }, 20 21 constructor: function () { 22 var me = this; 23 24 me.initConfig(arguments); 25 }, 26 27 eat: function () { 28 for (var i = 0; i < arguments.length; i++) { 29 console.log(arguments[i]); 30 } 31 } 32 }); 33 34 var user = Ext.create('User'); 35 36 user.setName('段光伟'); 37 38 user.play(); 39 user.work();
示例(C#扩展方法)
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace MixinDemo 8 { 9 public class User 10 { 11 public string Name { get; set; } 12 } 13 14 public static class Enjoyable 15 { 16 public static void Play(this User user) 17 { 18 Console.WriteLine(user.Name + "-play"); 19 } 20 } 21 22 public static class Workable 23 { 24 public static void Work(this User user) 25 { 26 Console.WriteLine(user.Name + "-work"); 27 } 28 } 29 }
示例(C#动态代理)
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 using Castle.DynamicProxy; 8 using Castle.DynamicProxy.Generators; 9 10 namespace MixinStudy 11 { 12 class Program 13 { 14 static void Main(string[] args) 15 { 16 var proxy = Factory.Create<User>(); 17 proxy.Id = Guid.NewGuid(); 18 proxy.Name = "段光伟"; 19 20 (proxy as ITeacher).Teach(); 21 (proxy as IFather).Play(); 22 } 23 } 24 25 [Mixin(typeof(Teacher))] 26 [Mixin(typeof(Father))] 27 public class User 28 { 29 public virtual Guid Id { get; set; } 30 public virtual string Name { get; set; } 31 } 32 33 public interface ITeacher 34 { 35 User User { get; set; } 36 37 void Teach(); 38 } 39 40 public class Teacher : ITeacher 41 { 42 [MixinTarget] 43 public User User { get; set; } 44 45 public void Teach() 46 { 47 Console.WriteLine("我教你读书吧:" + this.User.Name); 48 } 49 } 50 51 public interface IFather 52 { 53 User User { get; set; } 54 55 void Play(); 56 } 57 58 public class Father : IFather 59 { 60 [MixinTarget] 61 public User User { get; set; } 62 63 public void Play() 64 { 65 Console.WriteLine("我陪你玩吧:" + this.User.Name); 66 } 67 } 68 69 public static class Factory 70 { 71 public static T Create<T>(params object[] args) 72 where T : class 73 { 74 var generator = new ProxyGenerator(); 75 76 var options = new ProxyGenerationOptions(); 77 78 foreach (MixinAttribute attribute in typeof(User).GetCustomAttributes(true)) 79 { 80 var mixin = Activator.CreateInstance(attribute.MixinType); 81 82 options.AddMixinInstance(mixin); 83 } 84 85 var target = Activator.CreateInstance(typeof(T), args) as T; 86 87 var proxy = generator.CreateClassProxyWithTarget(target, options); 88 89 foreach (var mixin in options.MixinsAsArray()) 90 { 91 foreach (var property in mixin.GetType().GetProperties()) 92 { 93 if (property.GetCustomAttributes(typeof(MixinTargetAttribute), true).Any()) 94 { 95 property.SetValue(mixin, target); 96 } 97 } 98 } 99 100 return proxy; 101 } 102 } 103 104 [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] 105 public class MixinAttribute : Attribute 106 { 107 public MixinAttribute(Type mixinType) 108 { 109 this.MixinType = mixinType; 110 } 111 112 public Type MixinType { get; set; } 113 } 114 115 public class MixinTargetAttribute : Attribute 116 { 117 } 118 }
示例(C++)
class Enjoyable { public: Enjoyable(void); ~Enjoyable(void); void Play(); virtual string GetName() = 0; }; class Workable { public: Workable(void); ~Workable(void); void Work(); virtual string GetName() = 0; }; class User: public Enjoyable, public Workable { public: User(void); ~User(void); private: string name; public: string GetName(); void SetName(string name); }; User::User(void) { } User::~User(void) { } string User::GetName() { return this->name; } void User::SetName(string name) { this->name = name; } void Enjoyable::Play(){ cout << ( this->GetName() + "play"); } void Workable::Work(){ cout << ( this->GetName() + "work"); }
示例(Ruby)
1 module Enjoyable 2 def play 3 puts(self.name + "-play") 4 end 5 end 6 7 module Workable 8 def work 9 puts(self.name + "-work") 10 end 11 end 12 13 class User 14 include Enjoyable, Workable 15 16 attr_accessor :name 17 end 18 19 user = User.new 20 user.name = "段光伟" 21 user.play 22 user.work
代码示例(Java的Qi4j)
示例地址:http://qi4j.org/latest/two-minutes-intro.html
代码示例(Python)
user.py
class User(): def __init__(self): self.name = "unkwnow" from Enjoyable import play from Workable import work def getName(self): return self.name def setName(self, name): self.name = name user = User() user.setName("段光伟") user.play() user.work()
Enjoyable.py
def play(self): print(self.name + "-play")
Workable.py
def work(self): print(self.name + "-work")
备注
在学习一门语言的时候,如何模拟Mixin是我必须思考的一个东西。