面向对象基础(1)--继承 多态 重构

好久没有写博客了,最近一直忙着复习考研,前几天听了张老师讲的设计模式的这门课,很有感触,就简单地整理一下学的东西。。。


这节课讲的是面向对象的几个基本概念,老师举了个例子把面向对象的很多东西都连接起来了:动物比赛,每个不同动物都有名字,能发出叫声等。。。
一.继承.
             第一、子类拥有父类非private的属性和功能;
             第二、子类具有自己的属性和功能,即子类可以扩展父类没有的属性和功能;
             第三、子类还可以以自己的方式实现父类的功能(方法重写)
                     除了private和public外,还有protected表示继承时子类可以对父类有完全访问权。 也就是说,protected修饰的类成员,对子类公开,对其它类不公开。
     来看个例子:

猫类:         

class Cat
    {
        private int shoutNum = 3;
        public int ShoutNum
        {
            get
            {
                return shoutNum;
            }
            set
            {
                if (value < 10)
                {
                    shoutNum = value;
                }
                else
                {
                    shoutNum = 10;
                }
            }
        }
        private string name = "";
        public Cat(string name)
        {
            this.name = name;
        }
        public Cat()
        {
            this.name = "无名";
        }
        public string Shout()
        {
            string result = "";
            for (int i = 0; i < shoutNum; i++)
            {
                result += "喵 ";
            }
            return "我的名字叫" + name + " " + result;
        }
    }
狗类:

 class Dog
    {
        private int shoutNum = 3;
        public int ShoutNum 
        { 
            get
            {
                return shoutNum;
            }
            set 
            {
                if (value < 10)
                {
                    shoutNum = value;
                }
                else
                {
                    shoutNum = 10;
                }
            }
        }
        private string name = "";
        public Dog(string name)
        {
            this.name = name;
        }
        public Dog()
        {
            this.name = "无名";
        }
        public string Shout()
        {
            string result = "";
            for (int i = 0; i < shoutNum; i++)
            {
                result += "汪 ";
            }
            return "我的名字叫" + name + " " + result;
        }
    }
我们发现这两个类有很多相同的地方,于是我们把他们相同的地方都弄出来组成一个Animal类:

 class Animal
    {
        protected string name = "";//用了protected
        public Animal(string name)
        {
            this.name = name;
        }
        public Animal()
        {
            this.name = "无名";
        }

        protected int shoutNum = 3;
        public int ShoutNum
        {
            get
            {
                return shoutNum;
            }
            set
            {
                shoutNum = value;
            }
        }
    }
于是猫狗类就可以这样写:

    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;
        }
     }
    class Dog : Animal
    {
        public Dog() : base()
        { }

        public Dog(string name) : base(name)
        { }

        public  string Shout()
        {
            string result = "";
            for (int i = 0; i < shoutNum; i++)
                result += "汪 ";

            return "我的名字叫" + name + " " + result;
        }
    }

用UML类图表示:

继承优点:继承使得所有子类公共的部分都放在了父类,使得代码得到了共享,这就避免了重复,另外,继承可使得修改或扩展继承而来的实现都较为容易。

        缺点:父类变,则子类不得不变。继承会破坏包装,父类实现细节暴露给子类,这其实是增大了两个类之间的耦合性。


虽然采用了继承,但子类还是显得有些复杂,假如现在又有很多牛,羊什么的来了,那么这个Shout()函数就会有很多种,显得有点复杂,怎样解决???

二.多态.

          1.概念:表示不同的对象可以执行相同的动作,但要通过它们自己的实现代码来执行。

          2.举例:

                      有一位父亲是有名的艺术家, 六个儿子长大成人, 模仿父亲惟妙惟肖。 有一天,父亲突然发烧,上不了台,但是票已经卖了,退票影响声誉。于是决定让儿子化了                          妆代父亲表演。

                  有以下特点:

                  第一,子类以父类的身份出现
                          儿子代表父亲表演,化妆后就以父亲身份出现了。
                  第二,子类在工作时以自己的方式来实现
                          儿子模仿得再好,那也是模仿,儿子只能用自己方式表演父亲的作品。
                  第三,子类以父亲的身份出现时,子类特有的属性和方法不可以使用
                          儿子有自己的绝活也不能使用。

          3.虚方法和方法重写
                        虚方法:为了使子类的实例完全接替来自父类的类成员,父类必须将该成员声明为虚拟的。这是通过在该成员的返回类型之前添加virtual关键字来实现的。
                        方法重写:子类可以选择使用override关键字,将父类实现替换为它自己的实现,这就是方法重写override      

    class Animal
    {
        ……
        public virtual string Shout()//虚方法,没有实现具体功能
        {
            return "";
        }
    }

        class Cat : Animal//猫类
    {
        public Cat() : base()
        { }

        public Cat(string name) : base(name)
        { }

        public override string Shout()//方法重写
        {
            string result = "";
            for (int i = 0; i < shoutNum; i++)
                result += "喵 ";

            return "我的名字叫" + name + " " + result;
        }
    }

        class Dog : Animal//狗类
    {
        public Dog() : base()
        { }

        public Dog(string name) : base(name)
        { }

        public override string Shout()//方法重写
        {
            string result = "";
            for (int i = 0; i < shoutNum; i++)
                result += "汪 ";

            return "我的名字叫" + name + " " + result;
        }
    }
下面看怎么实例化来运用这些类的:

        private Animal[] arrayAnimal;
        private void btnRigister_Click(object sender, EventArgs e)
        {
            arrayAnimal = new Animal[5];
            arrayAnimal[0] = new Cat("小花");
            arrayAnimal[1] = new Dog("阿毛");
            arrayAnimal[2] = new Dog("小黑");
            arrayAnimal[3] = new Cat("娇娇");
            arrayAnimal[4] = new Cat(“小咪");
        }
        private void btnMatch_Click(object sender, EventArgs e)
        {
            foreach (Animal item in arrayAnimal)
            {
                MessageBox.Show(item.Shout());
            }
        }
运行结果:


来分析一下多态在内存的情况:

      多态的原理是当方法被调用时,无论对象是否被转换为其父类,都只有位于对象继承链最末端的方法实现会被调用。也就是说,虚方法是按照其运行时类型而非编译时类型进行动态绑定调用的。


当mybc调用print()函数时,首先找到父类的Print,父类的print指向子类的print,最终调用的是子类的print().


所以现在来了牛羊,我们只需要这样定义类:

   class Cattle : Animal
    {
        public Cattle() : base()
        { }

        public Cattle(string name) : base(name)
        { }

        public override string Shout()
        {
            string result = "";
            for (int i = 0; i < shoutNum; i++)
                result += “哞";

            return "我的名字叫" + name + " " + result;
        }
    }
    class Sheep : Animal
    {
        public Sheep() : base()
        { }

        public Sheep(string name) : base(name)
        { }

        public override string Shout()
        {
            string result = "";
            for (int i = 0; i < shoutNum; i++)
                result += “咩 ";

            return "我的名字叫" + name + " " + result;
        }
    }

我们发现虽然这样做了,但还是没有减少子类代码的复杂度,Shout()函数还在子类里,所以我们希望把Shout()函数弄到父类中去,从而引入重构的概念!

三.重构.

         1.概念:

                   两个互为兄弟的子类代码类似,并非完全相同首先采用Extract Method(提炼函数)区分相同部分和差异部分代码,构成单独函数然后再对提炼的代码使用Pull                               UpMethod(方法上移),将它移入父类

         2.对上述Animal进行改进,把子类中的shout()函数(相同部分)放到父类中,在父类中增加一个虚方法(不同部分,需子类实现)      

    class Animal
    {
        …… 
        public string Shout()
        {
            string result = "";
            for (int i = 0; i < shoutNum; i++)
                result += getShoutSound() + ",";

            return "我的名字叫" + name + " " + result;
        }

        protected virtual string getShoutSound()
        {
            return "";
        }
    }
    
    class Cat : Animal
    {
        public Cat() : base()
        { }

        public Cat(string name):base(name)
        {
        }

        protected override string getShoutSound()
        {
            return "喵";
        }
    }
        class Dog : Animal
    {
        public Dog() : base()
        { }

        public Dog(string name):base(name)
        {
        }

        protected override string getShoutSound()
        {
            return "汪";
        }
    }
这样一来我们的目的就达成了,,由于篇幅太长,分两篇写的,请看下篇!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值