设计原则学习

Posted 聆听微风

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计原则学习相关的知识,希望对你有一定的参考价值。

设计原则学习

(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类变化的情况就是给当前类添加新的功能时候。

(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)是指设计代码结构时,

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

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

   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)里式替换原则

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

(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. 注意:局部变量中的类,不是直接朋友

    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。
  • 优点:可以使系统更加灵活,降低类与类之间的耦合度,一个雷的变化对其他类造成的影响相对较少。

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

    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() + "颜色的汽车在行驶");
        
    

设计模式学习笔记 单一职责原则的判定与设计

1. SOLID原则

SOLID原则由5个原则组成,他们分别是:单一职责原则、开闭原则、里氏替换原则、接口隔离原则和依赖反转原则。

2. 单一职责原则

单一职责原则的英文是 Single Responsibility Principle,缩写为 SRP。这个原则的英文描述是这样的:A class or module should have a single reponsibility。如果我们把它翻译成中文,那就是:一个类或者模块只负责完成一个职责(或者功能)。

在单一职责原则中,一个类只负责完成一个职责或者功能。就是说,不要设计大而全的类,要设计粒度小、功能单一的类。

3. 如何判断类的职责是否足够单一?

不同的应用场景、不同阶段的需求背景下,对同一个类的职责是否单一的判定,可能都是不一样的。在某种应用场景或者当下的需求背景下,一个类的设计可能已经满足单一职责原则了,但如果换个应用场景或着在未来的某个需求背景下,可能就不满足了,需要继续拆分成粒度更细的类。

可参考的判定原则:
  • 类中的代码行数、函数或属性过多,会影响代码的可读性和可维护性,我们就需要考虑对类进行拆分;
  • 类依赖的其他类过多,或者依赖类的其他类过多,不符合高内聚、低耦合的设计思想,我们就需要考虑对类进行拆分;
  • 私有方法过多,我们就要考虑能否将私有方法独立到新的类中,设置为 public 方法,供更多的类使用,从而提高代码的复用性;
  • 比较难给类起一个合适名字,很难用一个业务名词概括,或者只能用一些笼统的Manager、Context 之类的词语来命名,这就说明类的职责定义得可能不够清晰;
  • 类中大量的方法都是集中操作类中的某几个属性,比如,在 UserInfo 例子中,如果一半的方法都是在操作 address 信息,那就可以考虑将这几个属性和对应的方法拆分出来。
  • 代码行数在50~70行左右,属性在10个以内是比较合适的

4. 单一职责原则的优缺点

类职责单一,类依赖的和被依赖的其他类也会变少,减少了代码的耦合性,以此来实现代码的高内聚、低耦合。但是,如果拆分得过细,实际上会适得其反,反倒会降低内聚性,也会影响代码的可维护性。

以上是关于设计原则学习的主要内容,如果未能解决你的问题,请参考以下文章

尚硅谷设计模式学习---[设计模式七大原则]

架构学习分享:软件架构设计的三大原则

架构师内功心法之设计原则

学习设计模式 - 六大基本原则之里氏替换原则

学习设计模式 - 六大基本原则之依赖倒置原则

设计模式七大原则之开闭原则学习