【大话设计模式】面向对象基础之山寨AS版(二)

A.8多态
“下面我们再增加需求,如果我们要举办一个动物运动会,来参加的有各种各样的动物,其中有一项是‘叫声比赛’。界面上就是放两个按钮,一个是‘动物报名’,就是确定动物的种类和报名顺序,另一个是‘叫声比赛’,就是报名的动物挨个的叫出声音来比赛。注意来报名的是什么,我们并不知道。可能是猫,可能是狗,也可能是其他的动物,当然它们要会叫才行。”
“有点复杂,我出了会加两个按钮外,不知道改怎么做了。”
“分下一下,来报名的都是动物,而且都会叫,说明动物Animal类都有shout方法,所谓的‘动物报名’,其实就是建立一个动物对象数组,让不同的动物加入其中。所谓的‘叫声比赛’,其实就是遍历这个数组来让动物吗shout(),就可以了。但是就之前讲的知识,是不足以解决这个问题的,所以我们引入面向对象的第三打特性——多态。”
“啊,多态,多态在学校里听的是很多,但一直都不懂是什么东西。”
多态表示不同的对象可以执行相同的动作,但要通过它们自己的实现代码来执行。举个例子,方便理解。我们的国粹‘京剧’以前都是子承父业,代代相传的艺术。假设有这样一对父子,父亲是个非常有名的京剧艺术家,儿子长大成人,模仿父亲唱戏也惟妙惟肖。有一天,父亲病了,不能上台了,于是决定让儿子代父亲上台表演。”
“化妆后谁认识谁啊,只要唱的好就可以糊弄过去了。”
“是,这里我们要注意几点。第一,子类以父类的身份出现。第二,子类在工作时以自己的方式来实现,儿子模仿的再好,也是用自己的能力来表现父亲的作品。第三,子类以父类的身份出现时,子类特有的属性和方法不可以使用。儿子经过多年的学习,其实已经有了自己的创作自己的绝活,但在此时,代表父亲表演时,绝不能表现出来。这就是多态。”
“听听好像都懂,怎么用呢?”
“是呀,怎么用呢,我们还要了解一些概念,虚方法和方法重写,为了使子类的实例完全接替来着父类的类成员,父类必须将该成员声明为虚拟的。在C#里面我们通过在该成员的返回类型之前加virtual关键字来实现。Flash里有预留这个关键字,也就是说现在还没有virtual,以后有添加的意向。虽然不能添加virtual关键字,但是我们同样可以再子类里选择使用override关键字,将父类实现替换为它自己的实现,这就是方法重写Override,或者叫方法复写。我们来看一下例子。”
“由于CatDog类都有shout方法,只是叫的声音不同,所以我们可以让Animal有一个shout的方法,然后CatDog去重写这个方法,用的时候,就可以用猫和狗代替Animal叫唤,来达到多态的目的。”
Animal类:  

ContractedBlock.gif ExpandedBlockStart.gif Code
package  
{
    
public class Animal
    {
        
protected var _name:String;
        
protected var _shoutNum:uint=3;//注意这里的修饰符要改为protected,以便于子类的访问
        
        
public function Animal(name:String = "无名"
        {
            
this._name = name;            
        }

        
public function get shoutNum():uint {
            
return _shoutNum;
        }        
        
public function set shoutNum(value:uint):void {
            _shoutNum 
= value;
        }        
        
public function shout():String {//这就是虚方法了,注意他是有方法体的只不过没有具体的实现
            return "";
        }        
    }    
}

Cat类:

 

ContractedBlock.gif ExpandedBlockStart.gif Code
package  

{

         
public class Cat extends Animal

         {

                   
public function Cat(name:String = "无名"

                   {

                            
super(name);                

                   }

                                     

                   override 
public function shout():String {//override表示我们对父类方法的复写

                            var result:String 
= "";

                            
for (var i:uint = 0; i < _shoutNum; i++) {

                                     result
+="喵!"

                            }

                            
return "我的名字叫"+_name+","+result;

                   }                 

                   

         }

         

}

Dog类:

ContractedBlock.gif ExpandedBlockStart.gif Code
package  
{
    
public class Dog extends Animal
    {
        
public function Dog(name:String = "无名"
        {
            
super(name);        
        }
        
/*
        *但是对于Dog类,因为我们在初始化的时候,有给Dog起名字
        * 所以我们用super来调用Animal类的构造函数,而且必须要包含接收到的参数
        
*/        
        override 
public function shout():String {//override表示我们对父类方法的复写
            var result:String = "";
            
for (var i:uint = 0; i < _shoutNum; i++) {
                result
+="汪!"
            }
            
return "我的名字叫"+_name+","+result;
        }        
    }    
}

 

这是Main文档类

  

ContractedBlock.gif ExpandedBlockStart.gif Code
package  

{

         
import flash.display.Sprite;

         
import flash.events.MouseEvent;         

         
public class Main extends Sprite

         {

                   
private var _animalButton1:AnimalButton;

                   
private var _animalButton2:AnimalButton;

                   
private var _AnimalArray:Array;

                   
public function Main() 

                   {

                            _animalButton1 
= new AnimalButton("动物报名");

                            _animalButton2 
= new AnimalButton("叫声比赛");

                            addChild(_animalButton1);

                            addChild(_animalButton2);
                            

                            _animalButton1.addEventListener(MouseEvent.MOUSE_DOWN, onClick1);

                            _animalButton2.addEventListener(MouseEvent.MOUSE_DOWN, onClick2);

                                                      

                            _animalButton2.x 
= 300;
                             

                   }

                   
public function onClick1(e:MouseEvent):void {

                            _AnimalArray 
= new Array(5);

                            _AnimalArray[
0= new Cat("小花");

                            _AnimalArray[
1= new Dog("阿毛");

                            _AnimalArray[
2= new Dog("小黑");

                            _AnimalArray[
3= new Cat("娇娇");

                            _AnimalArray[
4= new Cat("咪咪");
                            

                            trace(
"有动物来报名啦!");

                   }
                   

                   
public function onClick2(e:MouseEvent):void {

                            
for (var item in _AnimalArray) {                               

                                     trace(_AnimalArray[item].shout());

                            }

                   }                   

         }         

}

“结果显示,先点击“动物报名”,然后“叫声比赛”,依次会输出五个动物的叫声。”

“我知道了,Animal相当与京剧表演的老爸,CatDog相当于儿子,儿子代表父亲表演shout,但Cat叫出来的是‘喵’,Dog叫出来的是‘汪’,这就是所谓的不同的对象可以执行相同的动作,但要通过他们自己的实现代码来执行。”

“说的好,是这个意思,不过一定要注意了,这个对象的声明必须是父类,而不是子类,实例化的对象是子类,这才能实现多态。多态的原理是当方法被调用的时候,无论对象是否被转换为父类,都只有位于对象继承最末端的方法实现会被调用。也就是说,虚方法是按照其运行时类型而非编译时类型进行动态绑定调用的。

“蔡老师,受教了。” 

A.9重构

“现在又来了小牛和小羊来报名,需要参加‘叫声比赛’,你如何做?”

“这个简单了,我现在再实现牛Cattle和羊sheep的类,让它们继承Animal就可以了。”

 

ContractedBlock.gif ExpandedBlockStart.gif Code
package  

{

         
public class Cattle extends Animal

         {

                   
public function Cattle(name:String = "无名"

                   {

                            
super(name);                

                   }
                                    

                   override 
public function shout():String {//override表示我们对父类方法的复写

                            var result:String 
= "";

                            
for (var i:uint = 0; i < _shoutNum; i++) {

                                     result
+="哞!"

                            }

                            
return "我的名字叫"+_name+","+result;

                   }  
         }        

}

“等等,你有没有发现,猫狗牛羊四个类,除了构造方法之外,还有重复的地方?”

“是呀,我发现了,shout里除了四种动物叫的声音不同外,几乎没有任何的差异了。”

“很好,所以我们要对重复的部分进行改造。首先我们把shout的方法体放到Animal类中,然后把叫的声音部分改成另一个方法getShoutSound就可以了!”

 

ContractedBlock.gif ExpandedBlockStart.gif Code
package  

{

         
public class Animal

         {

                   
protected var _name:String;

                   
protected var _shoutNum:uint=3;//注意这里的修饰符要改为protected,以便于子类的访问                   

                   
public function Animal(name:String = "无名"

                   {

                            
this._name = name;                         

                   } 

                   
public function get shoutNum():uint {

                            
return _shoutNum;

                   }                   

                   
public function set shoutNum(value:uint):void {

                            _shoutNum 
= value;

                   }
                 

                   
public function shout():String {//这就是虚方法了,注意他是有方法体的只不过没有具体的实现

                            var result:String 
= "";

                            
for (var i:uint = 0; i < _shoutNum; i++) {

                                     result
+="汪!"

                            }

                            
return "我的名字叫"+_name+","+result;

                   }
                   

                   
public function getShoutSound():String {

                            
return "";

                   }                   

         }         

}

“此时的子类就极其简单了,除了叫声的构造方法的不同,所有重复的都转移到父类了,真是漂亮至极。”

 

ContractedBlock.gif ExpandedBlockStart.gif Code
package  

{

         
public class Cat extends Animal

         {

                   
public function Cat(name:String = "无名"

                   {

                            
super(name);                

                   }
                                     

                   override 
public function getShoutSound():String 

                   {

                            
return "喵!";

                   }   
         }        

}

“有点疑问,这样改动,子类就没有

“你把继承的基本忘记了?继承的第一条是什么?”

“哈,是子类拥有所有父类非private的属性和方法。对对,由于子类继承父类,所以是publicshout方法是一定可以为所有子类所用的。”史熙高兴的说,“我渐渐能感受到面向对象编程的魅力了,的确是非常的简捷。由于不用重复,所以需求的改变都不会影响到其他类。”

“这里其实就是一个设计模式,叫模板方法。”

“啊,原来就是设计模式啊,Very Good,太棒了,我竟然学会了设计模式。”

“疯了?发什么神经啊。”小菜微笑道,“这才是知道了皮毛,得意什么,还早这呢。”

 

A.10抽象类

“我们再来观察,你会发现,Animal类其实根本就不可能实例化,你想,说一只猫什么样,可以想象,说new Animal(),级实例化一个动物。一个动物长什么样啊?”

“不知道,动物是一个抽象的名词,没有具体对象与之对应。”

“非常正确,所以我们完全可以考虑把实例化没有任何意义的父类,改成抽象类,同样的,对应Animal类的getShoutSound方法,其实方法没有任何意义,C#里可以用abstract关键字,将这个方法定义为抽象方法,在ASAdobe也有意预留了这个关键字,所以我们现在不用添加,同样可以实现抽象方法和抽象类。注意在AS里没有abstract来约束抽象类,所以我们定义的抽象类跟普通类是没有区别的,同样可以实例化。但是Adobe可能会在不久将这个关键字完善进去,所以抽象类的定义还是一定要知道,第一,抽象类不能实例化,刚才就说过,‘动物’实例化是没有任何意义的;第二,抽象方法是必须被子类重写的方法,不重写的话,它的存在又有什么意义呢?其实抽象方法可以被看成没有实现体的虚方法;第三,如果类中包含抽象方法,那么类就必须定义为抽象类,不论是否还包含其他一般的方法。

“这么说的话,一开始就可以把Animal类设成抽象类了,根本没有必要存在虚方法的父类,是这样吧?”

“是的,我们应该考虑让抽象类拥有尽量多的共同代码,拥有尽量少的数据。”

“那到底什么时候应该用抽象类呢?”

抽象类通常代表一个抽象概念,它提供一个继承的出发点,当设计一个新的抽象类时,一定是用来继承的,所以在一个继承关系形成的等级结构里面,树叶节点应该是具体类,而树枝节点应该是抽象类。也就是说,具体类不是用来继承的。我们作为程序设计者,应该要努力做到这一点。比如,若猫,狗,牛,羊是最后一级,那么它们就是具体类,但如果还有更下面一级的金丝猫继承于猫,哈巴狗继承于狗,就需要考虑把猫狗改成抽象类了,当然这也是需要具体情况具体分析了。”

2009011015214817.jpg

 

A.11接口

“在动物运动会里还有一个非常特殊的比赛项目,是为了给有特意功能的动物展示其特殊才能的。”

“哈,有特异功能?有意思,不知道是什么动物?”

“多的是啊,可以来比赛的比如有机器猫,孙悟空,猪八戒等”

“啊,这也算动物啊,都是人们虚构出来的东西。”

“让猫狗比赛叫声难道不是虚构吗?其实我们的目的ishi为了让两个动物尽量不相干而已。现在叮当从肚皮的口袋里变出东西,孙悟空可以拔毫毛变东西,八戒有三十六般变化。他们个属于猫,猴,猪,现在需要让他们比赛谁变东西的本领大。”

“变东西应该是叮当,悟空和八戒的行为方法,要想用多态,就得让猫,猴,猪有变出东西的能力,而为了具有更普遍的意义,干脆让动物具有变出东西的行为,这样就可以使用多态了。”

“哈哈,你犯了几乎所有学OOP的人都会犯的错误,变出东西是动物的方法吗?,如果是,那是不是所有的动物都必须具备变出东西的能力呢?”

“这个,确实不是,这其实只是三个特殊动物具备的方法,那应该如何做?”

“这时候我们就需要新的知识了,那就是接口interface接口是把隐式公共方法和属性组合起来,以封装特定功能的一个集合。一旦类实现了接口,类就可以支持接口做指定的所有属性和成员。声明接口在语法上与声明类完全相同,但不允许提供接口任何成员的执行方式。所以接口不能实例化,不能有构造方法和自己的,不能有修饰符,比如:publicprivate等,不能声明虚拟的或静态的鞥,还有实现接口的类就必须要实现接口的所有方法和属性。

“怎么接口这么麻烦啊。”

“要求是多了点,但一个类可以支持多的接口,多个类也可以支持相同的接口。所以接口的概念可以让用户和其他开发任意更容易理解其他人的代码。哦,对了,记住,接口的命名,前面要加一个大写字母‘I’,这是王八的屁股——规定。”

“听不懂啊,不如讲讲实例吧。”

“我们先创建一个接口,他是用来变东西用的,注意接口用interface声明,而不是class,接口名称前要加‘I,接口的方法或属性前面不能有修饰符,方法没有方法体。  

ContractedBlock.gif ExpandedBlockStart.gif Code
package  

{

         
public interface IChange 

         {

                   function ChangeThing(thing:String):String;

         }         

}

“然后我们来创建机器猫的类。”   

ContractedBlock.gif ExpandedBlockStart.gif Code
package  

{

         
public class MachineCat extends Cat implements IChange

         {                   

                   
public function MachineCat(name:String="无名"

                   {

                            
super(name);

                   }

                   
public function ChangeThing(thing:String):String {

                            
return super.shout() + "我有万能的口袋,我可以变出:" + thing;

                   }

         }         

}

“猴子的类

Monkey 和孙悟空的类 StoneMonkey 与上面的非常相似,在此省略。此时我们的文档类的代码可以添加一个‘变出东西’按钮,如下。”

ContractedBlock.gif ExpandedBlockStart.gif Code
package  

{

         
import flash.display.Sprite;

         
import flash.events.MouseEvent;

         
import flash.media.Microphone;

         

         
public class Main extends Sprite

         {

                   
private var _animalButton1:AnimalButton;

                   
private var _animalButton2:AnimalButton;

                   
private var _animalButton3:AnimalButton;

                   
private var _AnimalArray:Array;

                   
public function Main() 

                   {

                            _animalButton1 
= new AnimalButton("动物报名");

                            _animalButton2 
= new AnimalButton("叫声比赛");

                            _animalButton3 
= new AnimalButton("变出东西");

                            addChild(_animalButton1);

                            addChild(_animalButton2);

                            addChild(_animalButton3);

                            

                            _animalButton1.addEventListener(MouseEvent.MOUSE_DOWN, onClick1);

                            _animalButton2.addEventListener(MouseEvent.MOUSE_DOWN, onClick2);

                            _animalButton3.addEventListener(MouseEvent.MOUSE_DOWN, onClick3);                            

                            

                            _animalButton2.y 
= 50;

                            _animalButton3.y 
= 100;                              

                   }

                   
public function onClick1(e:MouseEvent):void {

                            _AnimalArray 
= new Array(6);

                            _AnimalArray[
0= new Cat("小花");

                            _AnimalArray[
1= new Dog("阿毛");

                            _AnimalArray[
2= new Cattle("小黑");

                            _AnimalArray[
3= new Sheep("娇娇");

                            _AnimalArray[
4= new Cat("咪咪");

                            

                            trace(
"有动物来报名啦!");

                   }

                   

                   
public function onClick2(e:MouseEvent):void {

                            
for (var item in _AnimalArray) {                               

                                     trace(_AnimalArray[item].shout());

                            }

                   }

                   

                   
public function onClick3(e:MouseEvent):void {

                            var mcat:MachineCat 
= new MachineCat("叮当猫");        

                            var wukong:StoneMonkey 
= new StoneMonkey("孙悟空");

                            trace(mcat.ChangeThing(
"各种各样的东西!"));

                            trace(wukong.ChangeThing(
"各种各样的武器!"));

                   }                   

         }         

}

“哦,我明白了,其实这和抽象类很类似,由于我们现在要让两个完全不想干的对象,叮当和悟空来做相同的的事情‘变出东西’,所以我们不得不让他们去实现做这件‘变出东西’的接口,这样的话,当我调用接口的‘变出东西’的方法时,程序就会根据我实现接口的对象来做出反应,如果是叮当,就是用万能口袋,如果是悟空,就是七十二变,利用了多态性完成了两个不同的对象本不可能完成的任务。但是我对抽象类和接口的区别还是很模糊。”

 

“问到点子上了,从表面上来说,抽象类可以给出一些成员的实现,接口却不能包含成员的实现,抽象类的抽象成员可被子类部分实现,接口的成员需要实现类完全实现,一个类只能继承一个抽象类,但可实现多个接口等等。但这都是从两者的形态上区分的。我觉得还有三点能帮助我们去区分抽象类和接口的。第一,类是对对象的抽象;抽象类是对类的抽象;接口是对行为的抽象。第二,如果跨越不同类的对象,可使用接口;对于一些相似的类对象,用继承抽象类。第三,从设计角度上将,抽象类是从子类种发现了公共的东西,泛化出父类,然后子类继承父类,而接口是根本不知子类的存在,方法如何实现还不确认,预先定义。

“恩,这么听着还是有点晕。”

“哈哈,好记性不如烂笔头,道理讲的再好,不如自己动手去敲敲,以后多多练习,你会慢慢理解的,好了今天就到这里吧,走,吃饭去!”

“好,我请你,蔡老师!!”

 

好了,鉴于版权的问题,就到这里吧,面向对象的编程基础都讲的差不多了,下面就是设计模式的应用了,【大话设计模式】确实是非常值得一读的好书,特别适合刚刚开始学习OOP的同学。

文章最后,号召大家支持正版【大话设计模式】,去书店买回来,慢慢啃,嘿嘿!

这是作者程杰的博客:http://cj723.cnblogs.com/

当然我的“山寨版”博客也欢迎大家常来浇水!

转载于:https://www.cnblogs.com/ladeng6666/archive/2009/01/10/1373291.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值