接口和抽象类

抽象类

类和方法前加上abstract就变成抽象的

抽象方法不包容任何实现代码,不能是private

无法使用new关键字直接创建抽象类的对象。只能实例化子类对象

包含抽象方法的类一定是抽象类,但抽象类中的方法不一定是抽象方法,抽象类中可以包容“普通的”方法。

抽象类专为做基类而生的

开闭原则(Open Close Principle)

开闭原则的意思是:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类。

using System;

namespace AbstractStudy
{
    //为做基类而生的“抽象类”与“开放/关闭原则”
    class Program
    {
        static void Main(string[] args)
        {
            Vehicle v = new Truck();
            v.Run();
        }
    }
    
    abstract class Vehicle 
    {
        public void Stop()
        {
            Console.WriteLine("Stopped!");
        }
        //抽象方法,不需要声明方法体,事实上就算不是抽象方法,是虚方法的话,方法体也用不上,所以建议直接使用抽象方法
        public abstract void Run();
    }
    class Car :Vehicle
    {
        public override void Run() 
        {
            Console.WriteLine("Car is running...");
        }
    }
    class Truck :Vehicle
    {
        public override void Run()
        {
            Console.WriteLine("Truck is running...");
        }

    }
}

此例中展示了抽象类的好处,满足了开闭原则,当程序需要扩展时,只需要添加子类或者重写抽象方法即可,不需要更改抽象类

接口:更抽象的抽象类

使用interface关键字定义接口,接口定义了所有类继承接口时应遵循的语法合同。接口定义了语法合同 "是什么" 部分,派生类定义了语法合同 "怎么做" 部分。

接口定义了属性、方法和事件,这些都是接口的成员。接口只包含了成员的声明。成员的定义是派生类的责任。接口提供了派生类应遵循的标准结构。

接口命名一般以I开头,成员全部public

接口就等于纯虚的抽象类(抽象类中的方法全是抽象的)

接口的好处1

using System;
using System.Collections;

namespace InterfaceStudy
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] nums1 = new int[] { 1, 2, 3, 4, 5 };
            ArrayList nums2 = new ArrayList { 1, 2, 3, 4, 5 };
            Console.WriteLine(Sum(nums1));
            Console.WriteLine(Avg(nums2));
        }
        //如果不使用接口,那么求和和求平均值要写四个方法,因为int和arraylist是强类型,不能通用
        //需求方的foreach要求数据能够被迭代
        //arraylist和int都遵守IEnumerable接口,可以被迭代
        static int Sum(IEnumerable nums) {
            int sum = 0;
            foreach (var n in nums) sum += (int)n;
            return sum;
        }
        static double Avg(IEnumerable nums)
        {
            int sum = 0; double count=0;
            foreach (var n in nums) { sum +=(int)n; count++; }
            return sum/count;
        }
    }
}

接口的好处2:

紧耦合的例子:

using System;
using System.Collections;

namespace InterfaceStudy
{
    class Program
    {
        static void Main(string[] args)
        {
            var engine = new Engine();
            var car = new Car(engine);
            car.Run(3);
            Console.WriteLine(car.Speed);
        }
    }
    //car和engine耦合,Car非常依赖于Engine,弊端很大
    class Engine
    {
        public int RPM { get; private set; }
        public void Work(int gas)
        {
            this.RPM = 1000 * gas;
        }
    }
    class Car 
    {
        private Engine _engine;
        public Car(Engine engine)
        {
            _engine = engine;
        }
        public int Speed { get; private set; }
        public void Run(int gas) {
            _engine.Work(gas);
            this.Speed = _engine.RPM / 100;
        }
    }
}

日常开发中要避免紧耦合

解决耦合的方法可以使用接口!

using System;
using System.Collections;

namespace InterfaceStudy
{
    class Program
    {
        static void Main(string[] args)
        {
            //诺基亚和爱立信实现了解耦合,只需改变调用手机的名字,而接口和类的代码不需要更改
            var user = new PhoneUser(new EricsssonPhone());
            var user = new PhoneUser(new NokiaPhone());
            user.UsePhone();
        }
    }
    //接口
    interface IPhone 
    {
        void Dail();
        void PickUp();
        void Send();
        void Receive();
    }
    class PhoneUser 
    {
        private IPhone _phone;
        public PhoneUser(IPhone phone)
        {
            _phone = phone;
        }
        public void UsePhone()
        {
            _phone.Dail();
            _phone.PickUp();
            _phone.Receive();
            _phone.Send();
        }
    }
	//诺基亚
    class NokiaPhone : IPhone
    {
        public void Dail()
        {
            Console.WriteLine("Nokia calling......");
        }

        public void PickUp()
        {
            Console.WriteLine("Hello!This is Tim!"); 
        }

        public void Receive()
        {
            Console.WriteLine("Nokia message ring......");
        }

        public void Send()
        {
            Console.WriteLine("Hello!");
        }

    }
	//爱立信手机
    class EricsssonPhone : IPhone
    {
        public void Dail()
        {
            Console.WriteLine("Ericssson calling......");
        }

        public void PickUp()
        {
            Console.WriteLine("Hi!This is Tim!");
        }

        public void Receive()
        {
            Console.WriteLine("Ericssson ring......");
        }

        public void Send()
        {
            Console.WriteLine("Good evening!");
        }
    }
}

接口,解耦在单元测试中的应用

先写出紧耦合的代码

using System;
using System.Collections;

namespace InterfaceStudy
{
    class Program
    {
        static void Main(string[] args)
        {
            var fan = new DeskFan(new PowerSupply());
            Console.WriteLine(fan.Work());
        }
    }
    //电源
    class PowerSupply
    {
        public int GetPower()
        { return 100; }
    }
    //电扇
    class DeskFan
    {
        private PowerSupply _powerSupply;
        public DeskFan(PowerSupply powerSupply)
        {
            _powerSupply = powerSupply;
        }
        public string Work()
        {
            int power = _powerSupply.GetPower();
            if (power < 0)
            {
                return "Won't work";
            }
            else if (power < 100)
            { return "Slow"; }
            else if (power < 200)
            { return "Work fine"; }
            else 
            { return "Warning!"; }
        }
    }
}
//此代码,如果需要测试不同电源电压时代码输出是否正确,那么就需要不断的更改PowerSupply类
//这样是不对的,因为类设计出来就不应该再被更改,所以需要接口来解耦合。
//更改成下面这样的代码
  interface IpowerSupply
    {
        int GetPower();
    }
    //电源
    class PowerSupply:IpowerSupply
    {
        public int GetPower()
        { return 100; }
    }
    //电扇
    class DeskFan
    {
        private IpowerSupply _powerSupply;
        public DeskFan(IpowerSupply powerSupply)
        {
            _powerSupply = powerSupply;
        }
//再写单元测试来测试代码是否正确
        

接口隔离原则

接口隔离原则(Interface Segregation Principle,ISP)要求程序员尽量将臃肿庞大的接口拆分成更小的和更具体的接口,让接口中只包含客户感兴趣的方法。

要为各个类建立它们需要的专用接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。

接口隔离原则和单一职责都是为了提高类的内聚性、降低它们之间的耦合性,体现了封装的思想,但两者是不同的:

  • 单一职责原则注重的是职责,而接口隔离原则注重的是对接口依赖的隔离。
  • 单一职责原则主要是约束类,它针对的是程序中的实现和细节;接口隔离原则主要约束接口,主要针对抽象和程序整体框架的构建。

单一职责原则

单一职责原则的定义

单一职责原则(Single Responsibility Principle,SRP)

又称单一功能原则,由罗伯特·C.马丁(Robert C. Martin)于《敏捷软件开发:原则、模式和实践》一书中提出的。这里的职责是指类变化的原因,单一职责原则规定一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分(There should never be more than one reason for a class to change)。

该原则提出对象不应该承担太多职责,如果一个对象承担了太多的职责,至少存在以下两个缺点:

  1. 一个职责的变化可能会削弱或者抑制这个类实现其他职责的能力;
  2. 当客户端需要该对象的某一个职责时,不得不将其他不需要的职责全都包含进来,从而造成冗余代码或代码的浪费。

单一职责原则的优点

单一职责原则的核心就是控制类的粒度大小、将对象解耦、提高其内聚性。如果遵循单一职责原则将有以下优点。

  • 降低类的复杂度。一个类只负责一项职责,其逻辑肯定要比负责多项职责简单得多。
  • 提高类的可读性。复杂性降低,自然其可读性会提高。
  • 提高系统的可维护性。可读性提高,那自然更容易维护了。
  • 变更引起的风险降低。变更是必然的,如果单一职责原则遵守得好,当修改一个功能时,可以显著降低对其他功能的影响。
posted @ 2021-12-07 10:57  王ちゃん  阅读(46)  评论(0)    收藏  举报