c#之类,抽象类,接口

最近在看完刘铁猛老师讲解的类,抽象类,接口后,有一种豁然开朗的感觉,现将内容记录下来,以便后续的学习巩固。
先提一下程序设计的六大模式之一的开闭原则(open closed principle):用抽象构建架构,用实现扩展原则;(总纲)
设计模式文章的连接如下:(https://blog.csdn.net/luguojiu/article/details/102551573)
在后面的代码重构中会体现这一原则。

一天,小菜与大鸟在一起。
大鸟:小菜,现在有一个项目要交给你。项目要求写一个小汽车,这个汽车具有Run,以及Stop方法,
小菜:很简单呀,看我的。
几分钟后,小菜写出了代码,如下所示:

namespace Example_03
{
    class Program
    {
        static void Main(string[] args)
        {
        }
    }

    class Car
    {
        public void Run()
        {
            Console.WriteLine("Car is running");
        }
        public void Stop()
        {
           Console.WriteLine("Stopped !");
        }
    }
}

大鸟:嗯,不错,是这样的。那现在项目中要增加卡车了,和小汽车具有相同的方法。
小菜:简单,我粘贴复制,修改一下就可以了。
很快,代码如下

namespace Example_03
{
    class Program
    {
        static void Main(string[] args)
        {
        }
    }

    class Car
    {
        public void Run()
        {
            Console.WriteLine("Car is running");
        }
        public void Stop()
        {
            Console.WriteLine("Stopped !");
        }
    }

    class Truck
    {
        public void Run()
        {
            Console.WriteLine("Truck is running");
        }
        public void Stop()
        {
            Console.WriteLine("Stopped !");
        }
    }
}

大鸟:你这样写是没问题的,但是,在代码编写的过程中,尽量不要出现代码的粘贴复制现象。同时,你看看,Car类和Truck类中的方法都是一样的,这样写是不是重复了,还有就是,如果我需要添加更多的车的时候,是不是还需要添加更多的相同代码,这样是比较麻烦的。当多个类中相同代码较多的时候,可以考虑将相同代码放在父类当中
小菜:明白了。
几分钟后:

namespace Example_03
{
    class Program
    {
        static void Main(string[] args)
        {
            //客户端代码(体现了多态)
            Vechile vh1 = new Car();
            vh1.Run("Car");
            vh1.Stop();
            Vechile vh2 = new Truck();
            vh2.Run("Truck");
            vh2.Stop();

            Console.ReadLine();
        }
    }

    class Vechile
    {
        public void Run(string type)
        {
            if (type=="Car")
            {
                Console.WriteLine("Car is running");
            }
            else if (type == "Truck")
            {
                Console.WriteLine("Truck is running");
            }
        }
        public void Stop()
        {
            Console.WriteLine("Stopped !");
        }
    }
    class Car:Vechile
    {

    }

    class Truck:Vechile
    {

    }

运行结果:
在这里插入图片描述
大鸟:不错。我看到你在基类中改变了Run的方法,以前是不带参数的。
小菜:没办法啊,Car 和Truck的Run方法中的方法体是有区别的。
大鸟:嗯,你这样处理也是可以的。那如果我让你添加一个RaceCar呢?
小菜:我只需要在基类中的Run方法里添加一个else if 的分支,先后写一个RaceCar子类就行了。看我的,
一分钟后,小菜的代码如下:

namespace Example_03
{
    class Program
    {
        static void Main(string[] args)
        {
            //客户端代码(体现了多态)
            Vechile vh1 = new Car();
            vh1.Run("Car");
            vh1.Stop();
            Vechile vh2 = new Truck();
            vh2.Run("Truck");
            vh2.Stop();

            Console.ReadLine();
        }
    }

    class Vechile
    {
        public void Run(string type)
        {
            if (type=="Car")
            {
                Console.WriteLine("Car is running");
            }
            else if (type == "Truck")
            {
                Console.WriteLine("Truck is running");
            }
            else if (type=="RaceCar")
            {
                Console.WriteLine("RaceCar is running");
            }
        }
        public void Stop()
        {
            Console.WriteLine("Stopped !");
        }
    }
    class Car:Vechile
    {

    }

    class Truck:Vechile
    {

    }
    class RaceCar : Vechile
    {

    }
}

大鸟:嗯,这样写是可以的。但是,小菜啊,你知不知道你违反了一个程序设计中的一个很重要的原则呀。我先不告诉你,先来说一下存在的问题。按照你的这种写法,如果后续需要添加别的车的时候,你是不是需要频繁的去修改基类Vechile中的代码?
小菜:这个确实是的呀,但是那也没办法啊,有需求了就要改的。如果我想增加属性的话,我不也得去修改这个类啊?
大鸟:没错,如果你需要增加类的属性,确实是需要修改这个类,这个是必然的,同时,如果你需要给类添加新的功能,比如说,车还有加油的功能,那你需要在Vechile中添加Fill函数,这也是需要修改类的,这个也是必然,也是允许的。也就是说,你需要增加类的新功能的时候,修改类是正确的,除此之外,最好不要总去动类中的代码,比如说,增加一种类型的车,你就去修改Run中的方法,这个是不好的。
小菜:我似乎有些明白了。
大鸟:这么说吧,在一个团队了,你负责设计了这个类,然后团队中的另外一个成员需要继承你的这个基类,那么他还得跑到你那边去修改基类的代码,这显然不是好的体验。好的体验应该是,我直接继承,其他的我无需关心。
小菜:懂了。
大鸟:很好,这就是我今天要和你说的设计模式中的开闭原则。你刚刚的代码就是违反了这个设计原则啊。
小菜:那我该如何进行修改呢?
大鸟:首先啊,在设计Run方法时,我们可以考虑将基类的Run方法与子类中的方法具有相同的签名,。。。。
小菜:对,对,然后我将方法设计成虚方法,在子类中进行重写不就可以了嘛;
大鸟:是滴
几分钟后:
小菜改完了代码,如下:

namespace Example_03
{
    class Program
    {
        static void Main(string[] args)
        {
            //客户端代码(体现了多态)
            Vechile vh1 = new Car();
            vh1.Run();
            vh1.Stop();
            Vechile vh2 = new Truck();
            vh2.Run();
            vh2.Stop();

            Console.ReadLine();
        }
    }

    class Vechile
    {
        public virtual void Run()
        {
            Console.WriteLine("Vechile is running");
        }
        public void Stop()
        {
            Console.WriteLine("Stopped !");
        }
    }
    class Car:Vechile
    {
        public override void Run()
        {
            Console.WriteLine("Car is running");
        }
    }

    class Truck:Vechile
    {
        public override void Run()
        {
            Console.WriteLine("Truck is running");
        }
    }
}

运行结果:
在这里插入图片描述
大鸟:很好,运行结果与前面一样。在基类中设计虚方法,在子类中进行重写(override)。小菜,你发现没,基类中的虚方法,在实际运行中,一直不会执行到的。
小菜:确实是这样的哈,
大鸟:那这样的话,我们还要他的函数体干啥啊,不如就不要函数实现了啊
小菜:不要函数体了,也就是说函数没有实现,那这个函数就太抽象了。
大鸟:对啊,是很抽象的。这就成了抽象方法了,类也就变成抽象类了。。
小菜:我明白了,我这就去重构代码:
几分钟后:

namespace Example_03
{
    class Program
    {
        static void Main(string[] args)
        {
            //客户端代码(体现了多态)
            Vechile vh1 = new Car();
            vh1.Run();
            vh1.Stop();
            Vechile vh2 = new Truck();
            vh2.Run();
            vh2.Stop();

            Console.ReadLine();
        }
    }

    abstract class Vechile
    {
        public abstract  void Run();
        public void Stop()
        {
            Console.WriteLine("Stopped !");
        }
    }
    class Car : Vechile
    {
        public override void Run()
        {
            Console.WriteLine("Car is running");
        }
    }

    class Truck:Vechile
    {
        public override void Run()
        {
            Console.WriteLine("Truck is running");
        }
    }
}

大鸟:嗯,不错,是这样的。从上面 可以看出,只要类包含了抽象方法,那么这个类就是抽象类。当然了,抽象类中还可以包含非抽象方法的。
小菜:嗯,嗯。那如果我把所有的方法都写成抽象函数呢?
大鸟:你可以试一试啊。
小菜:好的。
几分钟后;

namespace Example_03
{
    class Program
    {
        static void Main(string[] args)
        {
            //客户端代码(体现了多态)
            Vechile vh1 = new Car();
            vh1.Run();
            vh1.Stop();
            Vechile vh2 = new Truck();
            vh2.Run();
            vh2.Stop();

            Console.ReadLine();
        }
    }

    abstract class Vechile
    {
        public abstract  void Run();
        public abstract void Stop();
    }
    class Car : Vechile
    {
        public override void Run()
        {
            Console.WriteLine("Car is running");
        }

        public override void Stop()
        {
            Console.WriteLine(" Car Stopped !");
        }
    }

    class Truck:Vechile
    {
        public override void Run()
        {
            Console.WriteLine("Truck is running");
        }

        public override void Stop()
        {
            Console.WriteLine(" Truck Stopped !");
        }
    }
}

大鸟:对的,是这样的。在之类中,基类中的全部的抽象函数在子类中必须全部实现,一个都不能少。
小菜:那如果我不小心少实了怎么办?
大鸟:这样的话,继承的这个类依然是抽象类。也就是说,一**个类没有完全实现其基类中的抽象方法的话,这个类也是抽象类。**下面,我在你的代码基础上进行修改一下,你看看。
几分钟后,大鸟的代码写好了,看下面:

namespace Example_03
{
    class Program
    {
        static void Main(string[] args)
        {
            //客户端代码(体现了多态)
            Vechile vh1 = new Car();
            vh1.Run();
            vh1.Stop();
            Vechile vh2 = new Truck();
            vh2.Run();
            vh2.Stop();

            Console.ReadLine();
        }
    }

    abstract class VechileBase
    {
        public abstract void Run();

    }

    abstract class Vechile:VechileBase
    {
        public abstract void Stop();
    }
    class Car : Vechile
    {
        public override void Run()
        {
            Console.WriteLine("Car is running");
        }

        public override void Stop()
        {
            Console.WriteLine(" Car Stopped !");
        }
    }

    class Truck:Vechile
    {
        public override void Run()
        {
            Console.WriteLine("Truck is running");
        }

        public override void Stop()
        {
            Console.WriteLine(" Truck Stopped !");
        }
    }
}

大鸟:看到了吧,VechileBase是Vechile的 基类,Vechile没有实现VechileBase中的Run方法,因此,Vechile依然是抽象类。但是,最终这些抽象方法都是需要在之类中实现的
小菜:明白了。
大鸟:小菜啊,你看看,在前面你把Vechile这个抽象类中的方法都设计成了抽象方法,这样固然是可以的,但是在实际应用中,我们是不这样做的。这个类的方法全部是抽象的,因此这个类的抽象程度是很高的,这样的话,我们一般用接口。下面我来重构一下代码:

namespace Example_03
{
    class Program
    {
        static void Main(string[] args)
        {
            //客户端代码(体现了多态)
            IVechile vh1 = new Car();
            vh1.Run();
            vh1.Stop();
            IVechile vh2 = new Truck();
            vh2.Run();
            vh2.Stop();

            Console.ReadLine();
        }
    }

    //abstract class VechileBase
    //{
    //    public abstract void Run();

    //}

    interface IVechile
    {
        void Stop();
        void Run();
    }
    class Car : IVechile
    {
        public void Run()
        {
            Console.WriteLine("Car is running");
        }

        public void Stop()
        {
            Console.WriteLine(" Car Stopped !");
        }
    }

    class Truck : IVechile
    {
        public void Run()
        {
            Console.WriteLine("Truck is running"); 
        }

        public void Stop()
        {
            Console.WriteLine(" Truck Stopped !");
        }
    }
}

大鸟:在接口里,方法也是没有任何实现的。只是定义,没有任何实现。接口只管有什么(what),具体的怎么做(how),接口是不管的接口中只能包含方法,属性,索引器等抽象的东西
小菜:嗯,嗯,终于明白啦。

大鸟:整个的实现过程就是这样的,小菜啊,你一定要理解这些代码在设计之初到最终的重构过程。只有真正理解了,才能在以后的设计过程过程中灵活的应用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值