Fork me on GitHub
复用离不开反射和IOC

现在就让我们一起来看一下什么是多态以及如何实现多态。

       多态就是不同的对象收到相同的消息时会产生不同的行为。同一个类在不同的场合下表现出不同的行为特征。

       多态的作用:把不同的子类对象当做父类来看,可以屏蔽不同的子类对象之间的差异,写出通用的代码,做出通用的编程,增加程序的灵活性和可扩展性,以适应需求的不断变化。

       如何实现多态呢?

       首先实现多态的条件就是继承(或实现接口)。实现多态的方式可以有以下三种方式:

       1、父类成员用virtual关键字修饰,子类可以重写父类成员(此处所指的成员均为子类可以继承的成员)

       2、父类成员用abstract关键字修饰,子类可以重写父类的成员而且必须重写(最终)。

       3、实现接口,子类可以实现接口中定义的方法而且必须实现。

       一、使用virtual实现多态

       用virtual修饰的方法称为虚方法。

       1、当父类有方法需要让子类重写时,则可以将此方法标记为virtual。

       2、虚方法在父类中必须要给出实现,哪怕是空实现。

       3、子类可以重写父类的虚方法,也可以不重写。

       下面我们就来看一个小例子:

复制代码
 1         static void Main(string[] args)
 2         {
 3             object str = "你好";
 4             object person = new Person() { Name="张三" };
 5             Console.WriteLine(str.ToString());             //此处输出什么?
 6             Console.WriteLine(person.ToString());  //此处输出什么?
 7             Console.ReadKey();
 8         }
 9 
10         public class Person
11         {
12             public string Name
13             {
14                 get;
15                 set;
16             }
17         }
复制代码

         聪明的你是不是已经知道了输出的结果呢?没错,第一行输出“你好”,第二行输出“命名空间.Person”,其中的命名空间据你的项目而定,同样都是object对象,为什么会出现这种情况呢?要解决这个问题,我们先得了解一下ToString()方法。
         ToString()是object类定义的一个虚方法,其实现的功能为把当前对象的类型装换为字符串输出。那么第一行为什么没有输出对象的类型呢?以为string类重写了ToString()方法,把当前对象输出了(即把string对象本身输出)。由于Person类没有重写ToString()方法,所以输出的是当前对象的类型。

         下面我们把Person类的代码稍稍改动一下。

复制代码
        public class Person
        {
            public string Name
            {
                get;
                set;
            }

            public override string ToString()
            {
                return this.Name;
            }
        }
复制代码

        此时第二行就会输出“张三”。

        二、使用abstract实现多态

       先来看一下有关抽象类的相关概念:

        1、abstract关键字不光可以修饰类成员,还可以修饰类。被abstract修饰的类称为抽象类,被abstract修饰的方法称为抽象方法。

        2、抽象类不能被实例化,如果要想实例化,则必须有子类继承他,并且抽象类变量只能指向实现了他中的所有抽象成员的子类对象。

        3、抽象类的子类如果没有实现他的抽象方法,或者只是实现了一部分,则这个类也必须是抽象类。

        4、如果一个类中包含抽象方法,则这个类必须声明为抽象类。反之,抽象类中不一顶含有抽象方法。

        5、抽象方法必须不能给出任何实现。

        6、抽象类既可以包含抽象成员,又可以包含具体代码成员。

        7、抽象类不能用static或sealed修饰。抽象方法不能用static或private修饰。

        8、抽象类中有构造函数,而且可以重载。

        说了好多,我都有点凌乱了,哈哈……

        记得曾经有人和我争论一个问题,说抽象类可以不被子类继承(很纠结的问题),这句话百分之百是对的,但是我们在项目中定义一个抽象类,而不用子类去继承他,那么它的存在还有什么意义呢?既然我们定义了抽象类,说明我们一定要用他,既然要用它就必须要有子类继承他(因为他本身不能被实例化)。(个人观点,有些偏激)

        下面我们来做一个抽象类的小例子:

        “橡皮鸭子(RubberDuck)、真实的鸭子(RealDuck)。两个鸭子都会游泳,而橡皮鸭子和真实的鸭子都会叫,只是叫声不一样,橡皮鸭子“唧唧”叫,真实地鸭子“嘎嘎”叫”

复制代码
        public abstract class Duck
        {
            public string Name
            {
                get;
                set;
            }

            public void Swim()
            {
                Console.WriteLine("我是{0},I can swimming!",this.Name);
            }

            public abstract void Speek();

        }

        public class RubberDuck:Duck
        {
            public RubberDuck(string name)
            {
                this.Name = name;
            }

            public override void Speek()
            {
                Console.WriteLine("唧唧...");
            }
        }

        public class RealDuck : Duck
        {
            public RealDuck(string name)
            {
                this.Name = name;
            }

            public override void Speek()
            {
                Console.WriteLine("嘎嘎...");
            }
        }

        static void Main(string[] args)
        {
            Duck duck1 = new RubberDuck("RubberDuck");
            Duck duck2 = new RealDuck("RealDuck");
            duck1.Swim();
            duck1.Speek();
            duck2.Swim();
            duck2.Speek();
            Console.ReadKey();
        }
复制代码

          至于输出结果,大家在脑子中输出一下吧。。。

          三、使用接口实现多态

          神马是接口?

          接口就是一种规范,一种协议,约定好遵守某种协议就可以写出通用的代码。

          1、接口中定义了若干个具有各自功能的方法,只是表示一种能力,并没有具体实现。

          2、接口中的成员不能有访问修饰符,默认public。

          3、接口中可以有属性,方法,索引器,事件等(归根结底都是方法),但是不能有字段(可以有静态)。

          4、实现接口的子类必须实现该接口的全部成员。(直接子类)

          5、一个类可以同时实现多个接口,接口也可以继承接口。

          6、接口不能被实例化。

          7、如果继承接口的类不想全部实现接口中的成员,则可以把这个类声明为抽象类,把不实现的方法声明为抽象方法。

          8、如果一个类同时继承了类和实现了接口,则必须把类放在前面。

          是不是又凌乱了呢?好吧,先说这么多吧。

          有时候我们总是觉得既然有了抽象类,还要接口做什么呢?其实接口是有他的存在意义的。比如我们看下面的例子:

          “一架直升飞机和一只麻雀都会飞,但是一架直升飞机属于飞机类,一只麻雀属于鸟类,飞机不具备鸟类的共有属性,同理,鸟类也不具有飞机类的共有特性,因此他们不能抽象一个共有的父类(否则从他们的父类派生的子类就会同时具备飞机和鸟类的全部特性,这会造成子类冗余)。那么在这种情况下,我们要想对”飞“这个能力实现多态,该怎么办呢?对,用接口。

复制代码
        public class Plane
        {
            public string Name
            {
                set;
                get;
            }

            public void LoadPeople()
            {
                Console.WriteLine("我可以载人");
            }
        }

        public class Bird
        {
            public string Name
            {
                set;
                get;
            }

            public void Raise()
            {
                Console.WriteLine("我可以繁殖后代");
            }
        }

        public interface IFlyable
        {
            void Fly();
        }

        public class VertiPlane:Plane,IFlyable
        {
             public void Fly()
             {
                 Console.WriteLine("我是{0},I can Flying!",this.Name);
             }
        }

        public class Sparrow : Plane, IFlyable
        {
            public void Fly()
            {
                Console.WriteLine("我是{0},I can Flying!", this.Name);
            }
        }
        static void Main(string[] args)
        {
            IFlyable plane = new VertiPlane() { Name="直升飞机"};
            IFlyable bird = new Sparrow { Name = "麻雀" };
            plane.Fly();
            bird.Fly();
            Console.ReadKey();
        }
复制代码

           此时如果我们要用这两个对象的其他属性怎么办呢?可以显示转换为想要的类型(前提是他指向的对象必须是将要转换的类型)

从本文标题中可以看出,主要说的是反射技术和控制反转(IOC)技术,本文主要先介绍一下我对这两种技术的理解及它们的优缺点,最后再用实例来说一下使用方法。

反射:可以使用反射动态创建类型的实例,将类型绑定到现有对象,或从现有对象获取类型并调用其方法或访问其字段和属性。这里,它最重要的是“动态性”,即根据条件动态创建“指定类型”的“实例”。

1 // Using GetType to obtain type information:
2 int i = 42;
3 System.Type type = i.GetType();
4 System.Console.WriteLine(type);

结果是:

System.Int32

本示例使用静态方法 GetType(Object 基类派生的所有类型都继承该方法) 获取变量类型的简单反射实例

1 // Using Reflection to get information from an Assembly:
2 System.Reflection.Assembly o = System.Reflection.Assembly.Load("mscorlib.dll");
3 System.Console.WriteLine(o.GetName());

结果是:

mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

本示例使用反射获取已加载的程序集的完整名称

反射一般用在以下情况中:

  • 需要访问程序元数据的属性。linq to sql 中使用很多

  • 执行后期绑定,访问在运行时创建的类型的方法。与工厂模式一起使用,根据配置文件中的类型来动态建立实例

IOC:(Inversion of Control,英文缩写为IoC)是一个重要的面向对象编程的法则来削减计算机程序的耦合问题。 控制反转还有一个名字叫做依赖注入(Dependency Injection)。简称DI。实现IOC的架构有很多如:Avalon 、Spring、JBoss及Unity等。

理解IOC:可以把IoC模式看做是工厂模式的升华,可以把IoC看作是一个大工厂,只不过这个大工厂里要生成的对象都是在XML文件中给出定义的,然后利用Java 的“反射”编程,根据XML中给出的类名生成相应的对象。

实现非常简单,根据容易名称去创建对象即可

复制代码
 1     /// <summary>
 2     /// The static factory of container
 3     /// </summary>
 4     public sealed class ContainerManager
 5     {
 6         /// <summary>
 7         /// Creates the specified container instance .
 8         /// </summary>
 9         /// <param name="containerName">Name of the container.</param>
10         /// <returns></returns>
11         public static IContainerContext GetContainer(string containerName)
12         {
13             return new UnityContainerContext(containerName);
14         }
15     }
复制代码

以下是在实际项目中的使用,IOC架构是用Unity,它的基础代码是:

复制代码
  1     /// <summary>
  2     /// The specific container context for Unity
  3     /// </summary>
  4     public class UnityContainerContext : ContainerContextBase
  5     {
  6         #region Fields
  7 
  8         /// <summary>
  9         /// The lock used for synchronous
 10         /// </summary>
 11         private static readonly object _synlock = new object();
 12 
 13         #endregion
 14 
 15         #region Constructor
 16 
 17         /// <summary>
 18         /// Initializes a new instance of the <see cref="UnityContainerContext"/> class.
 19         /// </summary>
 20         /// <param name="name">The name.</param>
 21         public UnityContainerContext(string name)
 22             : base(name)
 23         {
 24         }
 25 
 26         #endregion
 27 
 28         #region Properties
 29 
 30         /// <summary>
 31         /// Gets the current context.
 32         /// </summary>
 33         /// <value>The current context.</value>
 34         private HttpContext CurrentContext
 35         {
 36             get
 37             {
 38                 HttpContext context = HttpContext.Current;
 39                 if (context == null)
 40                 {
 41                     throw new Exception("The current httpcontext is null");
 42                 }
 43                 return context;
 44             }
 45         }
 46 
 47         #endregion
 48 
 49         #region Override Methods
 50 
 51         /// <summary>
 52         /// Initializes container.
 53         /// </summary>
 54         public override void Initialize()
 55         {
 56             OnBeforeInitialized(new ContainerEventArgs(this, ContainerType.Unity));
 57 
 58             if (CurrentContext.Application[Name] == null)
 59             {
 60                 lock (_synlock)
 61                 {
 62                     if (CurrentContext.Application[Name] == null)
 63                     {
 64                         IUnityContainer currentContainer = new UnityContainer();
 65                         UnityConfigurationSection section = ConfigurationManager.GetSection("unity") as UnityConfigurationSection;
 66                         section.Containers[Name].Configure(currentContainer);
 67                         CurrentContext.Application[Name] = currentContainer;
 68                     }
 69                 }
 70             }
 71 
 72             OnAfterInitialized(new ContainerEventArgs(this, ContainerType.Unity));
 73         }
 74 
 75         /// <summary>
 76         /// Resolves this instance.
 77         /// </summary>
 78         /// <typeparam name="T">Parameter type.</typeparam>
 79         /// <returns></returns>
 80         public override T Resolve<T>()
 81         {
 82             try
 83             {
 84                 Initialize();
 85 
 86                 IUnityContainer currentContainer = CurrentContext.Application[Name] as IUnityContainer;
 87                 return currentContainer.Resolve<T>();
 88             }
 89             catch(Exception ex)
 90             {
 91                 OnResolveFailed(new ContainerFailedEventArgs(this, ContainerType.Unity, ex));
 92                 return default(T);
 93             }
 94         }
 95 
 96         /// <summary>
 97         /// Tears down.
 98         /// </summary>
 99         public override void TearDown()
100         {
101             OnBeforeTearDown(new ContainerEventArgs(this, ContainerType.Unity));
102 
103             CurrentContext.Application[Name] = null;
104 
105             OnAfterTearDown(new ContainerEventArgs(this, ContainerType.Unity));
106         }
107 
108         #endregion
109 
110     }
复制代码

在项目中通过unity来创建对象的代码是:

复制代码
 1         /// <summary>
 2         /// 数据层实体的个性操作对象
 3         /// </summary>
 4         /// <typeparam name="TEntity"></typeparam>
 5         /// <returns></returns>
 6         protected TEntity LoadRepositoryEntity<TEntity>()
 7         {
 8             IContainerContext container = ContainerManager.GetContainer("repositoryContainer");
 9             return container.Resolve<TEntity>();
10         }
复制代码

这样,在BLL层调用DAL层对象时,可以通过LoadRepositoryEntity泛型方法来实现。

 

 
分类: 系统架构
posted on 2012-06-03 20:52  HackerVirus  阅读(229)  评论(0编辑  收藏  举报