天天看点

五分钟了解设计模式六大原则(上)简介单一职责原则(Single Responsibility Principle)里氏替换原则(Liskov Substitution Principle)迪米特法则 (Law Of Demeter)

目录

  • 简介
    • 设计模式是什么?
    • 设计模式六大原则是什么?
    • 设计模式有哪些?
  • 单一职责原则(Single Responsibility Principle)
    • 我们应该如何使用单一职责呢?
  • 里氏替换原则(Liskov Substitution Principle)
    • 什么是透明呢?
    • 我们应该如何使用里氏替换原则呢?
  • 迪米特法则 (Law Of Demeter)
    • 我们应该如何使用迪米特法则呢?

简介

实际上真实项目很难全部遵循,更多的时候会有一些侧重性,设计模式六大原则要能灵活应用,离不开实践的锤炼和思考,把这个真的融入到骨子里面去了,设计确实会不一样的
           

设计模式是什么?

面向对象语言开发过程中,遇到种种的场景和问题,提出的解决方案和思路,沉淀下来设计模式是解决具体问题的套路
           

设计模式六大原则是什么?

面向对象语言开发过程中,推荐的一些指导性原则没有明确的招数,而且也经常会被忽视/违背也是前辈总结,也是为了站在前辈的肩膀上
           

设计模式有哪些?

  1. 单一职责原则(Single Responsibility Principle)
  2. 里氏替换原则(Liskov Substitution Principle)
  3. 迪米特法则 (Law Of Demeter)
  4. 依赖倒置原则(Dependence Inversion Principle)
  5. 接口隔离原则(Interface Segregation Principle)
  6. 开闭原则 (Open Closed Principle)

单一职责原则(Single Responsibility Principle)

类T负责两个不同的职责:职责P1,职责P2。当由于职责P1需求发生改变而需要修改类T时,有可能会导致原本运行正常的职责P2功能发生故障。
           
{
     	 Animal animal = new Animal("鸡");//呼吸空气
         animal.Breath();
         //animal.Action();
     }
     {
         Animal animal = new Animal("牛");//呼吸空气
         animal.Breath();
         //animal.Action();
     }
     {
         Animal animal = new Animal("鱼");//呼吸水
         animal.Breath();
         animal.Action();
     }

           
public class Animal
    {
        private string _Name = null;
        public Animal(string name)
        {
            this._Name = name;
        }
        /// <summary>
        /// 这个方法就挺不稳定,经常各种分支变化经常修改
        /// </summary>
        public void Breath()
        {
            if (this._Name.Equals("鸡"))
                Console.WriteLine($"{this._Name} 呼吸空气");
            else if (this._Name.Equals("牛"))
                Console.WriteLine($"{this._Name} 呼吸空气");
            else if (this._Name.Equals("鱼"))
                Console.WriteLine($"{this._Name} 呼吸水");
            else if (this._Name.Equals("蚯蚓"))
                Console.WriteLine($"{this._Name} 呼吸泥土");
        }
         //应该拆分了
        public void Action()
        {
            if (this._Name.Equals("鸡"))
                Console.WriteLine($"{this._Name} flying");
            else if (this._Name.Equals("牛"))
                Console.WriteLine($"{this._Name} walking");
            else if (this._Name.Equals("鱼"))
                Console.WriteLine($"{this._Name} Swimming");
            else if (this._Name.Equals("蚯蚓"))
                Console.WriteLine($"{this._Name} Crawling");
        }

    }
           
单一职责就是一个类只负责一件事儿。面向对象语言开发中类是一个基本单位,单一职责原则就是封装的粒度

上面这个例子写分支判断,然后执行不同的逻辑,其实这就违背了单一职责原则,但是功能是可以实现的
           
{
        AbstractAnimal animal = new Chicken();
        animal.Breath();
        animal.Action();
    }
    {
        AbstractAnimal animal = new Fish();
        animal.Breath();
        animal.Action();
    }
           
public abstract class AbstractAnimal
    {
        protected string _Name = null;
        public AbstractAnimal(string name)
        {
            this._Name = name;
        }

        public abstract void Breath();
        public abstract void Action();
    }
           
public class Chicken : AbstractAnimal
    {
        public Chicken(string name) : base(name)
        {
        }

        public Chicken() : base("鸡")
        {
        }

        public override void Breath()
        {
            Console.WriteLine($"{base._Name} 呼吸空气");
        }
        public override void Action()
        {
            Console.WriteLine($"{base._Name} flying");
        }
    }
           
按父类+子类的拆分方式,每个类很简单,简单意味着稳定 意味着强大。	拆分后,也会造成代码量的增加,类多了,使用成本也高(理解成本)
           

我们应该如何使用单一职责呢?

如果类型足够简单,方法够少,是可以在类级别去违背单一职责。如果类型复杂了,方法多了,建议遵循单一职责原则。
           
  1. 方法级别的单一职责原则:一个方法只负责一件事儿(职责分拆小方法,分支逻辑分拆)
  2. 类级别的单一职责原则:一个类只负责一件事儿
  3. 类库级别的单一职责原则:一个类库应该职责清晰
  4. 项目级别的单一职责原则:一个项目应该职责清晰(客户端/管理后台/后台服务/定时任务/分布式引擎)
  5. 系统级别的单一职责原则:为通用功能拆分系统(IP定位/日志/在线统计)

里氏替换原则(Liskov Substitution Principle)

任何使用基类的地方,都可以透明(安全)的使用其子类。
什么是透明呢? 安全,不会出现行为不一致,下面会举例说明。
           
{
     	//People people = new Chinese();
         Chinese people = new Chinese();
         people.Traditional();
         //DoChinese(people);
         people.SayHi();
     }
     {
         Chinese people = new Hubei();
         people.Traditional();
         //DoChinese(people);
         people.SayHi();

     }
     {
         var people = new Hubei();
         people.Traditional();
         //DoChinese(people);
         people.SayHi();
     }
     {
         People people = new Japanese();
         people.Traditional();
         //要么上端调用去判断  不能写  因为Japanese没有Traditional
         //要么就只能在子类抛异常
     }
           
public class People
    {
        public int Id { get; set; }
        public string Name { get; set; }

        //abstract void Eat();
        public void Traditional()
        {
            Console.WriteLine("仁义礼智信 温良恭俭让 ");
        }
    }

    public class Chinese : People
    {
        public string Kuaizi { get; set; }
        public void SayHi()
        {
            Console.WriteLine("早上好,吃了吗?");
        }

    }

    public class Hubei : Chinese
    {
        public string Majiang { get; set; }
        public new void SayHi()
        {
            Console.WriteLine("早上好,过早了么?");
        }
    }

    //public class Animal//让People继承自Animal
    //{
    //    public int Id { get; set; }
    //    public string Name { get; set; }
    //}

    public class Japanese : People
    {
        //public int Id { get; set; }
        //public string Name { get; set; }
        //public new void Traditional()
        //{
        //    Console.WriteLine("忍者精神 ");
        //throw new Exception();
        //}
        //Traditional也会继承 但是Japanese又没有Traditional
        public void Ninja()
        {
            Console.WriteLine("忍者精神 ");
        }

    }
           

什么是透明呢?

在以上代码中我们可以看到Chinese people = new Hubei();  var people = new Hubei();这两个方法在调用SayHi方法时,结果是不同的。Chinese 调用的是父类的,var调用的是子类的。这样写会导致其他人再修改代码时掉入坑中,也是这个模式禁止的。	
           
五分钟了解设计模式六大原则(上)简介单一职责原则(Single Responsibility Principle)里氏替换原则(Liskov Substitution Principle)迪米特法则 (Law Of Demeter)

我们应该如何使用里氏替换原则呢?

  1. 父类有的,子类是必须有的;

    如果出现了子类没有的东西,那么就应该断掉继承;(再来一个父类,只包含都有的东西)

  2. 子类可以有自己的属性和行为

    子类出现的地方,父类不一定能代替

  3. 父类实现的东西,子类就不要再写了,(就是不要new隐藏)

    有时候会出现意想不到的情况,把父类换成子类后,行为不一致。如果想修改父类的行为,通过abstract/virtual

  4. 在声明属性、字段、变量,尽量声明在父类中。

迪米特法则 (Law Of Demeter)

迪米特法则(最少知道原则):一个对象应该对其他对象保持最少的了解。
           
public static void Show()
    {
        Console.WriteLine("************************");
        School school = new School()
        {
            SchoolName = "软谋教育",
            ClassList = new List<Class>()
            {
                new Class()
                {
                    ClassName="高级班",
                    StudentList=new List<Student>()
                    {
                        new Student()
                        {
                            StudentName="Tony"
                        },
                        new Student()
                        {
                            StudentName="风尘浪子"
                        }
                    }
                }
            }
        };

        school.Manage();
    }
           
/// <summary>
    /// 班级
    /// </summary>
    public class Class
    {
        public int Id { get; set; }
        public string ClassName { get; set; }

        public List<Student> StudentList { get; set; }


        public void ManageClass()
        {
            Console.WriteLine(" {0}Manage {1} ", this.GetType().Name, this.ClassName);
            foreach (Student s in this.StudentList)
            {
                s.ManageStudent();
                //Console.WriteLine(" {0}Manage {1} ", s.GetType().Name, s.StudentName);
            }

        }
    }
           
/// <summary>
    /// 学生
    /// </summary>
    public class Student
    {
        public int Id { get; set; }
        public string StudentName { get; set; }
        public int Height { private get; set; }

        public int Salay;

        public void ManageStudent()
        {
            Console.WriteLine(" {0}Manage {1} ", this.GetType().Name, this.StudentName);
        }
    }
           
School下有Class,Class下有Student。School不会直接与Student产生关系
           

我们应该如何使用迪米特法则呢?

1. 类与类之间的关系:
		纵向:继承≈实现(最密切)
		横向:聚合> 组合> 关联> 依赖(出现在方法内部)
2. 高内聚低耦合
		降低类与类之间的耦合,只与直接的朋友通信,就是要尽量避免依赖更多类型。(基类库(BCL--框架内置的)的类型除外,例如:string,int)
3. 工作中有时候会去造一个中介/中间层
		例如:门面模式 中介者模式  分层封装
					上层UI下订单---订单系统&支付系统&仓储&物流
					门面模式--上层交互门面--门面依赖子系统
					三层架构:UI---BLL---DAL
4. 去掉内部依赖,降低访问修饰符权限
		private,protected,internal,protected internal,public
5. 总之就是,依赖别人更少,让别人了解更少
           

继续阅读