设计原则:请重新审视“多重继承”,找机会拥抱一下“掺入(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是我必须思考的一个东西。

posted on 2013-04-25 07:53  幸福框架  阅读(2634)  评论(0编辑  收藏  举报

导航

我要啦免费统计