设计模式前奏–多态
这篇文章我将讲解多态,在上篇文章设计模式前奏–封装继承中我说过为什么要面向接口编程,而不是面向细节编程,在这篇文章中我将用示例来说明。
面向对象三大特性:封装、继承、多态。
首先说明重载和多态不是一个概念。
重载:名称相同,参数类型和参数个数不同
多态:“同参数、同返回类型。一个接口,多种实现”。
另外在多态中还有一个函数调用方式叫覆盖(Override),也有翻译为重写,和刚才上面提到重载不是一概念,这点务必重点区分,同时多态是判断一个面向对象编程语言是否属于高级语言的一个重要条件,有些面向对象编程语言就不支持多态。
重载(Overload),是指允许存在多个同名函数,而这些函数的签名也叫参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。其实,重载的概念并不属于“面向对象编程”,重载的实现是:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这样的)。
多态(Polymorphisn),按字面的意思就是“多种形状”。多态性是允许你将父对象设置成为和一个或更多的其子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是所有可以放父类对象的地方都可以用它的子类对象来代替,并且有着子类对象的特性,如属性和方法。面向对象设计模式有一个核心原则:里氏代换原则,就是说的这个。
好吧下面我们来看代码
class PC
{
private string name;
public PC(string name)
{
//this.name = name;
this.name = !string.IsNullOrEmpty(name) ? name : "unknown brand";
}
public virtual string Name { get { return name; } set { name = value; } }
//public abstract void Describe()
public virtual void Describe()
{
System.Console.WriteLine("PC: I am {0} personal computer", Name);
}
}
class Dell : PC
{
public Dell(string name) : base(name)
{
}
public override void Describe()
{
System.Console.WriteLine("Dell: I am {0} personal computer", Name);
}
}
class IBM : PC
{
private string name;
public override string Name { get { return name; } set { name = value; } }
public IBM(string name) : base(name)
{
this.name = name;
}
public override void Describe()
{
System.Console.WriteLine("IBM: I am {0} personal computer", Name);
}
}
class Program
{
static void Main(string[] args)
{
System.Console.WriteLine("Polymorphism \n");
PC pc = new PC("");
pc.Describe();
pc.Name = "xxPC";
pc.Describe();
Console.Write("\n");
PC dell = new Dell("Dell"); //面向接口编程
//Dell dell = new Dell("Dell");//面向细节编程
dell.Describe();
dell.Name = "OtherDell";
dell.Describe();
Console.Write("\n");
PC ibm = new IBM("IBM"); //面向接口编程
//IBM ibm = new IBM("IBM"); //面向细节编程
ibm.Describe();
ibm.Name = "OtherIBM";
ibm.Describe();
}
}
面向细节编程
//PC dell = new Dell("Dell");
Dell dell = new Dell("Dell");
//PC ibm = new IBM("IBM");
IBM ibm = new IBM("IBM");
面向接口编程
PC dell = new Dell("Dell");
//dell = new Dell("Dell");
PC ibm = new IBM("IBM");
//IBM ibm = new IBM("IBM");
请注意对比这二幅图,你会发现得到的结果是一样的。也许有同学会说这有什么意义,虽然实现的方式不一样,但得到结果都一样。恰恰问题的关键就在这里,也许现在你能不理解,那没关系,因为后继我的文章里会有关于设计模式的讲解,到时你就会清楚我现在这篇文章要诠释的意义。
以下代码我把各种修饰语句先去掉
class PC
{
private string name;
public PC(string name)
{
//this.name = name;
this.name = !string.IsNullOrEmpty(name) ? name : "unknown brand";
}
public string Name { get { return name; } set { name = value; } }
//public abstract void Describe()
public void Describe()
{
System.Console.WriteLine("PC: I am {0} personal computer", Name);
}
}
class Dell : PC
{
public Dell(string name) : base(name)
{
}
public void Describe()
{
System.Console.WriteLine("Dell: I am {0} personal computer", Name);
}
}
class IBM : PC
{
private string name;
public string Name { get { return name; } set { name = value; } }
public IBM(string name) : base(name)
{
this.name = name;
}
public void Describe()
{
System.Console.WriteLine("IBM: I am {0} personal computer", Name);
}
}
class Program
{
static void Main(string[] args)
{
System.Console.WriteLine("Polymorphism \n");
//PC pc = new PC("");
//pc.Describe();
//pc.Name = "xxPC";
//pc.Describe();
//Console.Write("\n");
//PC dell = new Dell("Dell");
Dell dell = new Dell("Dell");
dell.Describe();
dell.Name = "OtherDell";
dell.Describe();
Console.Write("\n");
//PC ibm = new IBM("IBM");
IBM ibm = new IBM("IBM");
ibm.Describe();
ibm.Name = "OtherIBM";
ibm.Describe();
}
}
//PC dell = new Dell(“Dell”);//代码定义
Dell dell = new Dell(“Dell”);//代码定义
//PC ibm = new IBM(“IBM”);
IBM ibm = new IBM(“IBM”);
运行后调用的是子类Dell和IBM
PC dell = new Dell(“Dell”);
//dell = new Dell(“Dell”);
PC ibm = new IBM(“IBM”);
//IBM ibm = new IBM(“IBM”);
运行后调用的是父类PC
这次发现二幅图终于有变化了吧
我们来看是修改了哪里
提示要使用关键字new
加上关键字并运行
//
//PC dell = new Dell(“Dell”);
Dell dell = new Dell(“Dell”);
//PC ibm = new IBM(“IBM”);
IBM ibm = new IBM(“IBM”);
//为了和上面对比,这里我使用橙色标记
//运行后调用的是子类Dell和IBM
//
PC dell = new Dell(“Dell”);
//dell = new Dell(“Dell”);
PC ibm = new IBM(“IBM”);
//IBM ibm = new IBM(“IBM”);
//使用绿色标记
运行后调用的是父类PC,和没加new前是一样的。
下面把关键字new换成override
提示”Dell.Describe()”:继承成员 PC.Describe()”未标记为virtual、abstract或override”,无法进行重写
把父类加上virtual
//PC dell = new Dell(“Dell”);
Dell dell = new Dell(“Dell”);
//PC ibm = new IBM(“IBM”);
IBM ibm = new IBM(“IBM”);
使用紫色标记
运行后调用的是子类Dell和IBM
PC dell = new Dell(“Dell”);
//dell = new Dell(“Dell”);
PC ibm = new IBM(“IBM”);
//IBM ibm = new IBM(“IBM”);
使用青色标记
运行后调用的同样是子类Dell和IBM
到这里你应该发现好像又回到最初我说的方式去了,就是面向接口编程。也许你现在还是不太能理解我说的意思,不过,没关系,因为如果你接触到工厂方法、抽象工厂、策略等一系列模式后你就明白我现在这个示例的意义所在。
关于上面提示”Dell.Describe()”:继承成员 PC.Describe()”未标记为virtual、abstract或override”,无法进行重写,我现在只做了修改为virtual的示例, 另外二种又是什么情况呢?如果非要说明父类除了使用virual,以及另外的abstract或override这二种情况,那么这个类的继承关系会更加复杂化,这篇文章我们就只讨论第一种情况。注意子类是new或override。
为进一步说明问题我再加入HP、Sony、Lenovo这三个对象
class Dell : PC
{
public Dell(string name) : base(name)
{
}
public override void Describe()
{
System.Console.WriteLine("Dell: I am {0} personal computer", Name);
}
}
class IBM : PC
{
private string name;
public override string Name { get { return name; } set { name = value; } }
public IBM(string name) : base(name)
{
this.name = name;
}
public override void Describe()
{
System.Console.WriteLine("IBM: I am {0} personal computer", Name);
}
}
class HP : PC
{
private string name;
public override string Name { get { return name; } set { name = value; } }
public HP(string name) : base(name)
{
this.name = name;
}
public new void Describe()
{
System.Console.WriteLine("HP: I am {0} personal computer", Name);
}
}
class Sony : PC
{
private string name;
public new string Name { get { return name; } set { name = value; } }
public Sony(string name) : base(name)
{
this.name = name;
}
public override void Describe()
{
System.Console.WriteLine("Sony: I am {0} personal computer", Name);
}
}
class Lenovo : PC
{
private string name;
public new string Name { get { return name; } set { name = value; } }
public Lenovo(string name) : base(name)
{
this.name = name;
}
public new void Describe()
{
System.Console.WriteLine("Lenovo: I am {0} personal computer", Name);
}
}
Dell dell = new Dell("Dell");
IBM ibm = new IBM("IBM");
HP hp = new HP("HP");
Sony sony = new Sony("Sony");
Lenovo lenovo = new Lenovo("Lenovo");
注意蓝色部分Sony,以及橙色部分HP(这里的HP输出是调用HP,请比对下图)
Sony:I am Sony personal computer
Sony:I am OtherSony personal computer
注意这里的HP我没做特别的标记,因为此时探讨Sony就已经很复杂。
PC dell = new Dell("Dell");
PC ibm = new IBM("IBM");
PC hp = new HP("HP");
PC sony = new Sony("Sony");
PC lenovo = new Dell("Lenovo");
注意绿色部分Sony,以及橙色部分HP(这里的HP输出是调用PC,请比对上图).
Sony:I am Sony personal computer
Sony:I am Sony personal computer
对比这二次Sony的调用有什么不同?
PC sony = new Sony(“Sony”);
Sony sony = new Sony(“Sony”);
注意子类(Sony class)里面new和override的地方,和其他子类对比下。
再回到前面看看我最初的代码,Dell和IBM这二个子类的差别,就是没加入对比前
为什么Dell只实现了一个Describe()方法,而IBM还多了一个Name{}属性。现在你知道我加入HP、Sony、Lenove这里对比的目的了吧。
通过示例可以看到子类IBM覆盖(override也叫重写)了父类PC中的Name{}属性,而Dell则完全没有去实现这个方法(Name{}属性),但却最终得到的结果却都 一样(这里必须强调此时IBM的Name{}属性的修饰符是override,而不是new。)。
可能有人会说这里是继承,但其实更多的是关于多态的特性,否则我也不会用这么多的示例和图片去说明问题,多态和继承本就是相关联的,没有继承关系就没有多态,也就是说多态是建立在继承的基础上的,就好比继承是建立在封装上一样的。先有封装,然后才有继承,最后才是多态。再次说明我这里虽然引入继承,但这里的重点不是说继承,而是讲解多态的复杂性,这个你要分清楚,当然你也可以把多态简单的理解为只是在子类中使用new或者override。
最后把基类改为抽象类
abstract class PC
{
private string name;
public PC(string name)
{
//this.name = name;
this.name = !string.IsNullOrEmpty(name) ? name : "unknown brand";
}
public virtual string Name { get { return name; } set { name = value; } }
//把Describe这个方法也改为抽象的,如果你的需求不是很特别,至少我认为在这个示例程序里应该是这样做
public abstract void Describe();
//public virtual void Describe()
//{
// System.Console.WriteLine("PC: I am {0} personal computer", Name);
//}
}
最后把pc = new PC(“”)的代码也注释掉,因为通常我们通常定义基类(或者父类)都是抽象的,就也是说不允许创建抽象类的实例。
//PC pc = new PC("");
//pc.Describe();
//pc.Name = "xxPC";
//pc.Describe();
PC dell = new Dell("Dell"); //面向接口编程
//Dell dell = new Dell("Dell");//面向细节编程
PC ibm = new IBM("IBM"); //面向接口编程
//IBM ibm = new IBM("IBM"); //面向细节编程
如果你还是没有弄清楚多态,或许你可以去看看这部影片《剌客摄隐娘》,感觉多态就像这部影片一样,有人说故事情节很吸引人环环相扣,有人说看不懂,甚至还有人看到一半就走了,哎,越扯越远了,呵呵。
言归正传,多态–这个面向对象编程领域的核心概念,本身的内容就很博大精深,要几句话说清楚实在是不太可能。
说了这么多不知道你理解我要表达的意图了吗?晚安!
track http://blog.csdn.net/cadenzasolo/article/details/50540089
版权所有,转载请注明文章出处 http://blog/csdn.net/cadenzasolo