学习设计模式之初,面向对象再理解

来源:互联网 发布:大学生兼职被骗数据 编辑:程序博客网 时间:2024/06/03 20:26

要开始大话设计模式的学习了,看这本书的封面,感觉很轻松,很卡通,很有趣。一本新书怎么去读,怎么去学,老师讲过很多次了。

一本书的前言是相当重要,它能帮助我们掌握全局,它也可能会给予我们一些指导。读完前言,我便知道自己下一步该做什么了。

下一个脚步——面向对象再理解。

关于面向对象,在学习C#的时候有了初步总结,但感觉很多概念理解得都不是很深刻。所以,在学习设计模式之初,很有必要再次进行总结。

从设计模式这本书中,故事性、提问性、交谈性的编写方式,读起来的感觉真的不太一样,没有那么枯燥,硬邦邦的感觉。

什么是对象?什么是类?就从动物运动会这个故事的开端说起了。

怎么才能实现猫叫?这个问题对于我们来说很容易啊,只要在点击按钮的事件下添加一行代码就可以了,

<span style="font-size:24px;"><span style="font-family:KaiTi_GB2312;font-size:18px;">private void button1_Click(object sender, EventArgs e)        {            MessageBox .Show ("喵");        }</span></span>
但这样做,很多问题就出现了。如果在其他事件下也需要实现猫叫,怎么办呢?这就引出了“类”,有了类,我们可以随时随地调用它。

<span style="font-size:24px;"><span style="font-family:KaiTi_GB2312;font-size:18px;">class Cat//定义猫类{    public string Shout()//类的方法    {        return "喵";    }</span></span>
我们该怎么去应用类呢?答案是将类实例化,这就引出了“对象”。接下来一个实例就是声明一个cat对象,让其实现猫叫。

<span style="font-size:24px;"><span style="font-family:KaiTi_GB2312;font-size:18px;">private void button1_Click(object sender, EventArgs e)        {            Cat cat = new Cat();//类实例化            MessageBox.Show(cat.Shout());        }</span></span>
什么是构造函数?什么是方法重载?我们的故事才刚刚开始。

人出生了,爸妈都会想着给孩子起个名字。动物也一样,特别是家里养得宠物,都有个可爱又洋气的名字。那么这就要用到构造函数了,即对类初始化。

所有的类都有构造方法,且与类同名,如果不定义构造方法,系统会默认生成空的构造方法。我们要给猫起个名字,就需要自己写一个构造方法。

<span style="font-size:24px;"><pre name="code" class="csharp"><span style="font-family:KaiTi_GB2312;font-size:18px;">class Cat//定义猫类{    private string name = "";//声明Cat类的私有字符串变量name    public Cat(string name)//定义Cat类的构造方法,参数为输入一个字符串    {        this.name = name;    }    public string Shout()//类的方法    {        return "我的名字叫" + name + " 喵";    }}</span></span>

<span style="font-size:24px;"><span style="font-family:KaiTi_GB2312;font-size:18px;">private void button1_Click(object sender, EventArgs e)        {            Cat cat = new Cat("咪咪");            MessageBox.Show(cat.Shout());        }</span></span>
这样我们就可以实现给猫起个名字叫“咪咪”。另外,一个类中构造函数的个数是可以多个的,可以根据需要删减,比如:我们也可以添加猫的出生日期等等。

但有时候,名字一开始还没有确定,这样不给出一个名字,就会出错了。这里我们就可以用重载的方法解决。同一个方法名,但参数类型不一样。这也验证了我的上一个说法,构造函数可以多个,下面,为了避免错误的法伤,我们又添加了一个构造函数:

<span style="font-size:24px;"><span style="font-family:KaiTi_GB2312;font-size:18px;">class Cat//定义猫类{    private string name = "";//声明Cat类的私有字符串变量name    public Cat(string name)//定义Cat类的构造方法,参数为输入一个字符串    {        this.name = name;    }    public Cat()//构造函数重载    {         this.name="无名";    }    public string Shout()//类的方法    {        return "我的名字叫" + name + " 喵";    }}</span></span>

故事仍在继续,下面是讲讲属性与修饰符的故事。

每个人,都有自己的身高、体重等,这就是对象的属性。以前写代码,写个对象“.”后面就会出现很多,其中就有各种属性。现在在类里面写属性了,感觉非常不一样。现在就以“猫叫的次数”属性为例。

<span style="font-size:24px;"><span style="font-family:KaiTi_GB2312;font-size:18px;"> private int shoutNum = 3;//声明默认叫的次数为3    public int ShoutNum    {        get        {            return shoutNum;//读属性        }        set        {            shoutNum = value;//写属性        }    }</span></span>

改进叫的方法:

<span style="font-size:24px;"><span style="font-family:KaiTi_GB2312;font-size:18px;"> public string Shout()//类的方法    {        string result = "";        for (int i = 0; i < shoutNum; i++)        {            result += "喵 ";        }        return "我的名字叫:" + name + "喵 " + result;    }</span></span>

调用:

<span style="font-size:24px;"><span style="font-family:KaiTi_GB2312;font-size:18px;">private void button1_Click(object sender, EventArgs e)        {            Cat cat = new Cat("小咪");//实例化一个小咪对象            cat.ShoutNum = 5;//给属性赋值            MessageBox .Show (cat.Shout());        }</span></span>

结果:


public和private都是修饰符,用以前的话说就是两者的作用域不同。现在具体来说,public表示它所修饰的类成员允许其他任何类访问,也就是公有的;private只允许同一个类中的成员访问,也就是私有的。

故事的开端好像有点长啊,不过这都是需要一步步慢慢来,一个个小故事慢慢讲,别着急,接下来,就是故事的高潮了,即面向对象的三大特性:封装、继承和多态。

先来说说封装的好处:可以减少耦合;类内部的实现可以自由修改;这一点,在敲机房系统的时候,有些体会。实现某个功能,把它写在模块中,在需要用的地方调用就好了,这就是封装的好处,减少了不少代码。我们实现了猫叫,那怎样实现狗叫呢?

现在这个工作就简单多了,仿造Cat类加一个Dog类,并添加相应的事件。

结果:


这样下来,又会发现,其中为了实现它们,其中还是包含很多重复的代码。我们又该如何去解决呢?这就需要讲到第二个特性继承了。

先从类的角度来说,猫和狗都是属于同一类,即哺乳动物。也可以换句话说,猫和狗与哺乳动物间属于继承关系。

下面给大家讲讲继承的工作方式:

定义父类(基类)和子类(派生类),其中子类继承父类的所有特性,同时也可以有自己新的特性。

学习继承需要记住的三句话:

如果子类继承与父类,那么

1.子类拥有父类非Private的属性和方法;

2.子类可以有自己的属性和方法;

3.子类可以以自己的方式实现父类的方法(方法重写)。

继承的格式:子类:父类,可用base关键字代表父类。

先对比上面写的Dog类和Cat类,可以发现,除了画线的那些地方,其他代码都是一样的:


下面就还以上面的例子,加上继承的思想重新编写代码。

先建立一个父类:Animal

<span style="font-size:24px;"><span style="font-family:KaiTi_GB2312;font-size:18px;">class Animal//定义动物类{    protected string name = "";        protected Animal(string name)        {        this.name = name;    }    public Animal ()    {        this.name = "无名";    }    protected int shoutNum = 3;    public int ShoutNum    {        get        {            return shoutNum;        }        set        {            shoutNum = value;        }    }}</span></span>

下面写Cat的代码,

<span style="font-size:24px;"><span style="font-family:KaiTi_GB2312;font-size:18px;">class Cat : Animal{    public Cat()        : base()    { }    public Cat(string name):base(name)    {}    public string Shout()    {        string result = "";        for (int i = 0; i < shoutNum; i++)            result += "喵, ";        return "我的名字叫" + name + " " + result;    }}</span></span>

Dog代码与之类似,把名称改了就行。

结果:

  

这样的话代码又简单了不少。下面接着讲第三个特性:多态。

这一次举的是“动物报名”和“叫声比赛”的例子。动物来了,它们就报上自己名字并发出自己的声音。如何去实现呢?

现在父类不上场了,就完全由子类自己去实现,但子类必须完全按照父类的方法去实现,这就需要为父类加“virtual”关键字,为子类加“override”关键字。

   

那除了猫和狗,要是其它动物来了,它们又该怎么办呢?比如说牛和羊。一想,只要再多加两个类就行了,并让它们继承父类Animal。可是,这样,代码又多了许多。这就需要讲讲重构了。我们就需要修改Shout方法,针对不同的动物,让它们自己返回不同的声音,这个新方法叫getShoutSound方法,这一过程就是重构了。

<span style="font-size:24px;">public string Shout()    {        string result = "";        for (int i=0;i<shoutNum ;i++)            result +=getShoutSound()+ ", ";        return "我的名字叫" + name + " "+result ;    }</span>

接下来是不同的子类,返回值各不相同,如狗类和羊类:
<span style="font-size:24px;">class Sheep : Animal{    public Sheep()        : base()    { }    public Sheep(string name)        : base(name)    { }    protected override string getShoutSound()    {        return "咩";    }}</span>
<span style="font-size:24px;">class Dog : Animal{    public Dog()        : base()    { }    public Dog(string name)        : base(name)    { }    protected override string getShoutSound()    {        return "汪";    }}</span>

  

故事的刚开始我们就谈了类,现在故事快接近尾声了,经过了前面那么多的实例,抽象类这个概念就更加容易理解了。之前我们定义了很多类,动物这个父类,猫、羊等子类,我们可以想象出一只猫的样子,可动物的样子是无法确定的,也就是无法对其进行实例化,所以它就是一个抽象类了,用关键字abstract表示。

什么是接口?它和抽象类有什么区别?接下来继续我们的故事。

说起铃铛猫和孙悟空,它们都有特异功能,会变东西。但两者是因为情况并不相同,叮当猫是因为有口袋,孙悟空是因为七十二变,要它们一起实现变出东西,就需要一个接口去帮助它们。所以,首先声明个接口:

<span style="font-size:24px;">interface IChange        {            string ChangeThing(string thing);        }</span>

然后机器猫通过接口实现变东西:

<span style="font-size:24px;">class MachineCat : Cat, IChange        {            public MachineCat()                : base()            {            }            public MachineCat(string name)                : base(name)            {            }            public string changeThing(string thing)            {                return base.Shout() + " 我有万能的口袋,我可变出: " + thing;            }        }</span>

抽象类和接口的区别:

1.抽象类是对类的抽象;

2.行为跨越不同类的对象,可使用接口;对于一些相似的对象,可用继承抽象类。

下面同样是讲两者的区别,一个是数组,一个是集合:

它们主要是容量区别,数组是固定的,而集合可根据需要自动扩充。

故事讲了很久,很累了,这么一看,又把面向对象以故事的方式又温习了一遍,可能对于某些东西还不是理解得很透彻,还需要日后多多体会才行。通过这些故事,通过故事中实例的实现,比起之前结合C#视频的总结,还是有所进步的。面向对象,以后还需要把你温习。

最后附上一张学习过后自己重新回想知识整理的一些概念:









0 0
原创粉丝点击