设计原则学习

设计原则学习

(1)单一职责原则

一个类应该只有一个发生变化的原因

当前类不符合单一职责原则:

高内聚:把模块内部相关联的元素聚集起来

当内部细节变化,需要对类的内部修改;

当整体变化(添加新功能)的时候,也需要对类的实现方法进行修改

    public class TelPhone
    {
        public void Dial(string phone_number) 
        {
            Console.WriteLine("给"+phone_number+"打电话");
        }

        public void HangUp(string phone_number)
        {
            Console.WriteLine("挂断" + phone_number + "打电话");

        }

        public void SendMessage(string message)
        {
            Console.WriteLine("发送" + message + "");

        }

        public void ReceiveMessage(string message) 
        {
            Console.WriteLine("接受" + message + "");

        }
    }

现在对代码进行修改:

    public interface IDial
    {
        void DialNumber(string phone_number);
    }

    public interface IHangUp
    {
        void HangUpNumber(string phone_number);
    }

    public interface ISendMessage
    {
        void SendMessage(string message);
    }

    public interface IReceiveMessage
    {
        void ReceiveMessage(string message);
    }

    public class Dial : IDial
    {
        public void DialNumber(string phone_number)
        {
            Console.WriteLine("给" + phone_number + "打电话");
        }
    }

    public class HangUp : IHangUp
    {
        public void HangUpNumber(string phone_number)
        {
            Console.WriteLine("挂断" + phone_number + "打电话");
        }
    }

    public class SendMessageClass : ISendMessage
    {
        public void SendMessage(string message)
        {
            Console.WriteLine("发送" + message + "");
        }
    }

    public class ReceiveMessageClass : IReceiveMessage
    {
        public void ReceiveMessage(string message)
        {
            Console.WriteLine("接受" + message + "");
        }
    }

    public class TelIPhone
    {
        private IDial _iDial;
        private IHangUp _iHangup;
        private ISendMessage _iSendMessage;
        private IReceiveMessage _iReceiveMessage;

        public void DialPhoneNumber(string phone_number)
        {
            _iDial.DialNumber(phone_number);
        }

        public void HangUpPhoneNumber(string  phone_number)
        {
            _iHangup.HangUpNumber(phone_number);
        }

        public void SendMessage(string message)
        {
            _iSendMessage.SendMessage(message);
        }
        public void ReceiveMessage(string message)
        {
            _iReceiveMessage.ReceiveMessage(message);
        }
    }

现在问题:如果DialPhoneNumber()方法变动,那么需要修改TelIPhone类吗?不用,TelIPhone类仅仅是调用,而非实现。如果要修改需要Dial类的方法。

那么引起TelIPhone类变化的情况就是给当前类添加新的功能时候。

image-20230406211518258

(2)开放封闭原则

软件实体(类、模块、函数等)应该可以拓展,但是不可以修改。即对拓展是开放的(open for extension),但是对更改是封闭的(closed for modification)

面向抽象/接口编程

把可能发生变化的地方使用 接口/抽象 进行封装

1.将BankProcess的执行方法 抽象为接口,然后将接口实现

    public partial class Program
    {
        static void Main(string[] args)
        {
            BankClient bankClient= new BankClient();
            bankClient.BankType = "存款";
            BankStuff bankStuff= new BankStuff();
            bankStuff.HandleProcess(bankClient);
        }
    }

    public interface IDeposite
    {
        void DepositeInterface();
    }

    public interface IDrawMoney
    {
        void DrawMoneyInterface();
    }

    public class DepositeClass : IDeposite
    {
        public void DepositeInterface()
        {
            Console.WriteLine("存款");
        }
    }


    public class DrawMoneyClass : IDrawMoney
    {
        public void DrawMoneyInterface()
        {
            Console.WriteLine("取款");
        }
    }

    public class BankProcess
    {
        public IDeposite deposite { get; set; }
        public IDrawMoney drawMoney { get; set; }

        public void DepositeFunc()
        {
            this.deposite.DepositeInterface();
        }

        public void DrawMoneyFunc()
        {
            this.drawMoney.DrawMoneyInterface();
        }

    }

    public class BankStuff
    {
        private BankProcess BankProcess = new BankProcess();
        public void HandleProcess(BankClient bankClient)
        {
            switch (bankClient.BankType)
            {
                case "存款":
                    BankProcess.deposite = new DepositeClass();
                    BankProcess.DepositeFunc();
                    break;
                default:
                    break;
            }
        }
    }

    public class BankClient
    {
        public string BankType;
    }

第二次修改:

将BankProcess的处理行为抽象为接口,然后让各种行为实现接口。调用的时候,使用对应的子类来初始化对象

       public partial class Program
    {
        static void Main(string[] args)
        {
            BankClient bankClient= new BankClient();
            bankClient.BankType = "存款";
            BankStuff bankStuff= new BankStuff();
            bankStuff.HandleProcess(bankClient);
        }
    }

    public interface IDrawMoney
    {
        void DrawMoneyInterface();
    }


    public class BankStuff
    {
        private IBankProcess _bankProcess ;
        public void HandleProcess(BankClient bankClient)
        {
            switch (bankClient.BankType)
            {
                case "存款":
                    _bankProcess = new DepositeClass();
                    _bankProcess.BankProcess();
                    break;
                default:
                    break;
            }
        }
    }

    public interface IBankProcess
    {
       public void BankProcess();
    }

    public class DepositeClass: IBankProcess
    {
        public void BankProcess()
        {
            Console.Write("存款");
        }
    }

    public class BankClient
    {
        public string BankType;
    }

目的:对BankClient类进行修改:

HandleProcess中的变化太多了。

封装,抽象不是目的,目的是封装变化。

只有把变化的地方进行封装,程序才能做到高内聚低耦合

    public partial class Program
    {
        static void Main(string[] args)
        {
            IBankClient bankClient = new DepositeClient();
            BankStuff bankStuff = new BankStuff();
            bankStuff.HandleProcess(bankClient);
        }
    }

    public interface IDrawMoney
    {
        void DrawMoneyInterface();
    }


    public class BankStuff
    {
        private IBankProcess _bankProcess;
        public void HandleProcess(IBankClient bankClient)
        {
            _bankProcess = bankClient.GetBankProcess();
            _bankProcess.BankProcess();

        }
    }

    public interface IBankProcess
    {
        public void BankProcess();
    }

    public class DepositeClass : IBankProcess
    {
        public void BankProcess()
        {
            Console.Write("存款");
        }
    }

    public class BankClient
    {
        public string BankType;
    }

    public interface IBankClient
    {
        // 封装的是变化
        // 根据不同需求的客户端,返回不同的处理对象/处理办法
        public IBankProcess GetBankProcess();
    }

    public class DepositeClient : IBankClient
    {
        public IBankProcess GetBankProcess()
        {
            return new DepositeClass();
        }
    }

(3)依赖倒置原则

依赖倒置原则(Dependence Inversion Principle,DIP)是指设计代码结构时,

高层模块不应该依赖低层模块,二者都应该依赖其抽象。

抽象不应该依赖细节,细节应该依赖抽象。

image-20230407004217274

   public partial class Program
    {
        static void Main(string[] args)
        {
            Singer singer = new Singer();
            singer.SingSong(new ChineseSong());
        }
    }

    public class Singer
    {
      
        public  void SingSong(ISong song)
        {
            Console.WriteLine("歌手 唱 " + song.GetSongWords());
        }
    }

    public interface ISong
    {
        public string GetSongWords();
    }

    public class ChineseSong: ISong
    {
        public string GetSongWords()
        {
            return "中国歌曲";
        }
    }

高层模式使用底层模式。所以高层模块(客户类)是Singer,底层模块(服务类)是Song。现在高层模式依赖了抽象的ISong接口,不再依赖具体的服务类,这样没办法在服务类的内部实例化一个ISong对象(SingSong方法)。所以我们在客户类提供了一个服务类的注入点ISong参数。让Program类在使用的时候,具体实例化服务类注入到客户类中。

依赖注入的方法:

  1. 接口注入

  2. 构造函数注入

  3. 属性的set注入

(4)里式替换原则

子类可以扩展父类的功能,但不能改变父类原有的功能

image-20230407011053717

(5)接口隔离原则

使用方不应该依赖于它不使用的方法

简单来说:把接口变小,把职责细分。(用于实现低耦合)

不要都实现了大接口中的方法,这样生成的类耦合性会很高

(6)迪米特原则

迪米特法则(Law of Demeter)又叫作最少知识原则(The Least Knowledge Principle),通俗的来讲,就是一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类来说,无论逻辑多么复杂,都尽量地的将逻辑封装在类的内部,对外除了提供的public方法,不对外泄漏任何信息。

比如关机操作,外部类只要调用Computer的关机方法就行了。没必要感知关机行为中的具体方法

    public class Person
    {
        public void CloseComputer(Computer computer)
        {
            computer.CloseComputer();
        }
    }

    public class Computer
    {
        private void SaveCurrentTask()
        {

        }

        private void CloseScreen()
        {

        }

        private void ShutDown()
        {

        }

        public void CloseComputer()
        {
            this.SaveCurrentTask();
            this.CloseScreen();
            this.ShutDown();
        }
    }

只和自己的直接朋友通信

  1. 成员对象
  2. 方法参数
  3. 方法返回值
  4. 注意:局部变量中的类,不是直接朋友

image-20230407014156829

    public partial class Program
    {
        static void Main(string[] args)
        {
            BranchOfficeManager branchOfficeManager = new BranchOfficeManager();
            new HeadOfficeManager().Print(branchOfficeManager);
        }
    }
    public class HeadOfficeEmployee
    {
        public int Id { get; set; }
    }

    public class BranchOfficeEmployee
    {
        public int Id { get; set; }
    }
    /// <summary>
    /// 总公司管理者
    /// </summary>
    public class HeadOfficeManager
    {
        /// <summary>
        /// 获取员工数
        /// </summary>
        /// <returns></returns>
        public List<HeadOfficeEmployee> GetEmployees()
        {
            List<HeadOfficeEmployee> headOffices = new List<HeadOfficeEmployee>();
            for (int i = 0; i < 10; i++)
            {
                HeadOfficeEmployee headOfficeEmployee = new HeadOfficeEmployee();
                headOfficeEmployee.Id = i;
                headOffices.Add(headOfficeEmployee);
            }
            return headOffices;
        }
        /// <summary>
        /// 打印总公司员工id
        /// </summary>
        public void Print(BranchOfficeManager branchOfficeManager)
        {
            var datas = GetEmployees();
            Console.WriteLine("总公司员工Id分别是:");
            foreach (var item in datas)
            {
                Console.WriteLine(+item.Id);
            }
            //branchOfficeEmployes这个集合对象,是通过局部变量的形式出现在类中的,所以不是我们的直接朋友,故不符合迪米特原则。
            var branchOfficeEmployes = branchOfficeManager.GetBranchOfficeEmployees();
            Console.WriteLine("分公司员工Id分别是:");
            foreach (var item in branchOfficeEmployes)
            {
                Console.WriteLine(+item.Id);
            }
        }
    }

    public class BranchOfficeManager
    {
        public List<BranchOfficeEmployee> GetBranchOfficeEmployees()
        {
            List<BranchOfficeEmployee> list= new List<BranchOfficeEmployee>();
            for (int i = 0; i < 5; i++)
            {
                BranchOfficeEmployee branchdOfficeEmployee = new BranchOfficeEmployee();
                branchdOfficeEmployee.Id=i;
                list.Add(branchdOfficeEmployee);
            }
            return list;
        }
    }

修改以后:

    public partial class Program
    {
        static void Main(string[] args)
        {
            new BranchOfficeManager().Print();
            new HeadOfficeManager().Print();
        }
    }
    public class HeadOfficeEmployee
    {
        public int Id { get; set; }
    }

    public class BranchOfficeEmployee
    {
        public int Id { get; set; }
    }


    public class BranchOfficeManager
    {
        public List<BranchOfficeEmployee> GetBranchOfficeEmployees()
        {
            List<BranchOfficeEmployee> list = new List<BranchOfficeEmployee>();
            for (int i = 0; i < 5; i++)
            {
                BranchOfficeEmployee branchdOfficeEmployee = new BranchOfficeEmployee();
                branchdOfficeEmployee.Id = i;
                list.Add(branchdOfficeEmployee);
            }
            return list;
        }

        public void Print()
        {
            var datas = GetBranchOfficeEmployees();
            Console.WriteLine("分公司员工Id分别是:");
            foreach (var item in datas)
            {
                Console.WriteLine(+item.Id);
            }
        }
    }


    /// <summary>
    /// 总公司管理者
    /// </summary>
    public class HeadOfficeManager
    {
        /// <summary>
        /// 获取员工数
        /// </summary>
        /// <returns></returns>
        public List<HeadOfficeEmployee> GetEmployees()
        {
            List<HeadOfficeEmployee> headOffices = new List<HeadOfficeEmployee>();
            for (int i = 0; i < 10; i++)
            {
                HeadOfficeEmployee headOfficeEmployee = new HeadOfficeEmployee();
                headOfficeEmployee.Id = i;
                headOffices.Add(headOfficeEmployee);
            }
            return headOffices;
        }
        /// <summary>
        /// 打印总公司员工id
        /// </summary>
        public void Print()
        {
            var datas = GetEmployees();
            Console.WriteLine("总公司员工Id分别是:");
            foreach (var item in datas)
            {
                Console.WriteLine(+item.Id);
            }
        }
    }

(7)合成复用原则

  • 定义:尽量使用对象组合/聚合,而不是继承关系达到软件复用的目的。
  • 聚合has-A和组合contains-A。
  • 优点:可以使系统更加灵活,降低类与类之间的耦合度,一个雷的变化对其他类造成的影响相对较少。

把已经有的对象,纳入新对象中,作为新对象的对象成员来实现的,新对象可以调用已有对象的功能,从而达到复用。

image-20230403173615376

    public partial class Program
    {
        static void Main(string[] args)
        {
            Car qyCar = new QYCar();
            qyCar.Move(new Red());
        }
    }

    public interface IColor
    {
        string ShowColor();
    }

    public class Red : IColor
    {
        public string ShowColor()
        {
            return "红色";
        }
    }

    public abstract class Car
    {
        public abstract void Move(IColor color);
    }

    public class QYCar : Car
    {
        public override void Move(IColor color)
        {
            Console.WriteLine(color.ShowColor() + "颜色的汽车在行驶");
        }
    }
posted @ 2023-04-07 02:23  聆听微风  阅读(47)  评论(0编辑  收藏  举报