设计模式的六大原则?

单一职责原则

有且只有一个原因会引起类的变化,即是说,一个类只会对一个职责内的事情负责。比如说权限类,那么他只负责权限内的所有事情,其他登录或者角色等相关的一些东西他不会干涉,也不要把权限的事情放到其他的类里面。一定要记住,手不要伸的太长,否则的话,相关的代码分散到系统的各个地方,维护的代码实在太多,随着你系统的扩张,维护的代码也会呈几何增长。
想象下中医药房,每个抽屉只放了一种药材,抓方子的时候很方便,直接抓起来称重量就可以了,如果多种药材放在了一起,就十分的麻烦了。

接口隔离原则
  • 接口的定义:
    • 1.实例接口:其实就是Java中的类,而你的具体事例必须遵守类,也就是调用方法,属性这些东西。你A类总不能调用B类的方法和属性变量把?
    • 2.类接口:也就是Java中interface关键字定义的接口。
  • 隔离的定义:
    • 1.客户端不应该依赖它不需要的接口
    • 2.类间的关系应该是建立在最下弄得接口上。
      两句话汇总起来就是:对于类接口来说客户端应该只需要它需要的接口,这个接口只做了客户端期望的事情,也就是说接口必须保持小而纯净,避免臃肿。而对于实际接口来说,一个接口中的方法必须尽量的少。
    • 这边和单一职责的区别就是单一职责原则要让类和接口的职责单一,注重的是职责,是业务上的划分,而接口隔离指的是接口职责来说的,也就是你的接口职责必须单一(接口只做自己该做的事情,需要小而纯净)。
      举个例子,你去吃饭,看到有收银员,厨师,服务员,洗碗阿姨等,类比接口隔离,这些人是一个一个的接口,而他们只做自己负责的事情,就是保持了接口的纯净。收银员只收钱而不会去洗碗。他们各司其职,而你需要干什么事情了就只会去找对应的人。
  • 那么我们如何保证接口的纯净呢?
    • 1.接口要尽量的小
      这是接口隔离原则的核心定义,不要出现臃肿的接口,但是小也是有限度的,细化接口可以帮助我们将代码逻辑隔离,但是同时也带来了接口泛滥导致难以维护的代价。
    • 2.接口要高内聚:
      高内聚就是要提高类,接口,模块的处理能力,也就是尽量减少对外公开方法,当你对外方法越少,变更的风险就越少。
    • 3.定制服务
      定制服务就是单独为一个个体提供优良的服务,类比上面的例子就是收银员可能对你是收银,退款服务,而对他老板来说就是报账,核账服务了。因为不同的个体的需求是不同的,就算目前一样,但是对于以后来说你能保证永远一样么?
    • 4.接口的设计是有限度的
里氏替换原则

里氏替换原则原则建立在继承之上,牢牢记住A extends B,B可以是实现类或者是抽象类。
目的是为了让子类更好的复用自父类继承而来的方法。通俗说在任何父类的地方都可以被替换成子类,并且程序不会有报错和出错。有四条规则规定了继承之间的限制:

  • 1.之类必须完全实现父类的方法,但不得重写(覆盖)父类的非抽象(已实现)方法。
  • 2.子类可以有自己的个性
  • 3.覆盖或实现父类的方法时,方法的前置条件(即方法的形参)要比- 父类方法的输入参数更宽松。(即只能重载不能重写)
  • 4.覆盖或者实现父类的方法时,方法的后置条件(即方法的返回值)要比父类更严格

解释这4个限制:

  • 首先,调用实例方法必须是可以被new的,因此A中必然是实现了B的抽象方法。子类不能覆写父类已实现的方法。父类中已实现的方法其实是一种已定好的规范和契约,如果我们随意的修改了它,那么可能会带来意想不到的错误。
  • 第二,子类当然可以有自己独有的方法
  • 第三,由于在父类的地方可以被无痕的替换为子类,因此子类最好不要去覆盖父类中的方法,如果真的要覆盖的话。那么,子类的输入参数的范围就必须大于父类输入参数的范围。当真正调用的时候,你传入的是HashMap的实例,就重载调用父类的method方法,而不会去调用子类的method方法。这样程序原本的规则就没有任何的变化。举个例子。
  • 第四,子类的覆盖方法的返回值必须是父类方法的子类。
//对于第三个限制的例子:
public class Test1 {
    public static class Father{
        public Collection doSomething(HashMap map){
            System.out.println("父类被执行了。。。");
            return map.values();
        }
    }
    public static class Son extends Father{
        //方法参数类型
        public Collection doSomething(Map map){
            System.out.println("子类被执行了。。。");
            return map.values();
        }
    }

    public static void main(String[] args) {
        Father s = new Father();
        HashMap map = new HashMap();
        s.doSomething(map);
        Son s1 = new Son();
        HashMap map1 = new HashMap();
        s1.doSomething(map1);
    }
}
/*
    * 结果为:
    * 父类被执行了。。。
    * 父类被执行了。。。
    * */
依赖倒置原则

定义:高层模块不应该依赖底层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象

  • 什么是高层模块,底层模块?
    底层原子性的操作就是底层模块,以底层模块为基础构建新的模块的就是高层模块
  • 抽象和细节?
    抽象就是java中的抽象类和接口,细节就是具体的实现类。
public class Java {
    public void say(){
        System.out.println("程序员用java语言在编程!");
    }
}
public class Programmer {
    //高层逻辑依赖了底层的原子逻辑,此时两个类耦合了,没有任何扩展而言
    public void programming(Java java){
        java.say();
    }
}

此时程序员类只能说Java语言,不能说其他语言,但是通过学习是可以掌握其他语言技能的。所以和实际的设计不符合

public interface Language {
    public void say();
}
public class Java implements Language{
    @Override
    public void say(){
        System.out.println("程序员用java语言在编程!");
    }
}
public class Python implements Language{
    @Override
    public void say() {
        System.out.println("程序员用Python语言在编程!");
    }
}
public class Programmer {
    public void programming(Language language){
        language.say();
    }
}

测试:

public class Test {
    public static void main(String[] args) {
        Programmer programmer = new Programmer();
        programmer.programming(new Java());
        programmer.programming(new Python());
    }
}

输出:

程序员用java语言在编程!
程序员用Python语言在编程

依赖倒置原则的核心就是面向接口(language),这个例子中,高层模块(Programmer)不应该依赖底层模块(Java,Python),同时,细节应该依赖与抽象。

依赖倒置拥有良好的扩展和健壮性,以后不管是什么语言只要继承了Language,程序员既可以使用这么语言。并且程序员不需要做任何的改变。


依赖其实就是类中的实现的继承关系,那么什么是倒置呢?
现实中,我们都是依托于客观具体的事务存在的,摸得着看得见的,比如电脑,汽车等等,在上面的例子就是具体的Java,Python类,而依赖倒置就是扭转了这种思想,不要依赖于具体实例,而是拥抱抽象,也就是接口,上面的例子就是Language接口。这就是倒置的由来。

迪米特法则

定义:迪米特法则(Law of Demeter)又叫做最少知识原则(Least Knowledge Principle 简写为LKP),就是说一个对象应当对其他对象有尽可能少的了解(不和陌生人说话)。

  • ①只与你直接的"朋友"们通信
  • ②不要和"陌生人"说话
  • ③每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。

关于"朋友"的定义:
首先来解释下什么是直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖、关联、组合、聚合等。其中,当前对象本身(this)、成员变量、以参数形式传入当前对象方法中的对象、方法返回值中的类,当前对象创建的对象为直接的朋友。

简单的说就是不要过多的去调用在方法中定义的类的方法,反过来,类不要过多的暴露方法给外部,暴露的越多,类之间的耦合度就越高,当类改动的越大,风险就越大。过多的方法之间的调用,最后任何一方的改动就会影响到另外一方,就越复杂。

//明星
public class Star {
    private Broker broker;
    public Star(Broker broker){
        this.broker = broker;
    }
    public Broker getBroker(){
        return broker;
    }
    public void setBroker(Broker broker){
        this.broker = broker;
    }
    public void play(String activity){
        Merchant merchant = new Merchant(activity);
        broker.reservation(merchant);
        System.out.println("明星去演出了");
    }
}
//经纪人
public class Broker {
    public void reservation(Merchant merchant){
        System.out.println("经纪人预约");
        merchant.business();
    }
}
//商家
public class Merchant {
    private String activity;
    public Merchant(String activity){
        this.activity = activity;
    }
    public void business(){
        System.out.println("和商家洽谈"+activity+"活动");
    }
}
public class Test {
    public static void main(String[] args) {
        Star star = new Star(new Broker());
        star.play("慈善");
    }
}

结果:

经纪人预约
和商家洽谈慈善活动
明星去演出了

上面的代码没有任何问题可以输出,但是仔细想想,现实中,有那个明星演出预约是自己接触商家?直接都是让经纪人一个人去谈,然后谈妥了。钱到位了,那么明星才真正的开始演出的。再说,明星那么忙,怎么可能每个演出都去和商家接触一下,那不是要忙死了?因此,这里明显的明星和商家不恰当的耦合了,他不应该接触商家,代码改为如下。

//明星
public class Star {
    private Broker broker;
    public Star(Broker broker){
        this.broker = broker;
    }
    public Broker getBroker(){
        return broker;
    }
    public void setBroker(Broker broker){
        this.broker = broker;
    }
    public void play(String activity){
        //直接和经纪人说去洽谈演出,和谁接触我不管
        broker.reservation();
        System.out.println("明星去演出了");
    }
}
//经纪人
public class Broker {
    private Merchant merchant;
    public Merchant getMerchant(){
        return merchant;
    }
    public void setMerchant(Merchant merchant){
        this.merchant = merchant;
    }
    public void reservation(){
        System.out.println("经纪人预约");
        merchant.business();
    }
}
//商家
public class Merchant {
    private String activity;
    public Merchant(String activity){
        this.activity = activity;
    }
    public void business(){
        System.out.println("和商家洽谈"+activity+"活动");
    }
}
public class Test {
    public static void main(String[] args) {
        Merchant merchant = new Merchant("慈善");
        Broker broker = new Broker();
        broker.setMerchant(merchant);
        Star star = new Star(broker);
        star.play("慈善");
    }
}

结果:

经纪人预约
和商家洽谈慈善活动
明星去演出了

这样一来,明星不和商家接触,只和关系亲密的经纪人接触就可以了,好处就是当活动有任何的变更时,影响范围只会波动到经纪人,并不会对明星有任何的影响,明星和商家直接就没有耦合关系了。


迪米特法则的核心就是降低系统之间类的耦合度。只有弱耦合了,类的复用性才能提高,相对的,会有中间类的出现,如上面的例子的经纪人就是一个中间人的角色,导致了请求在系统中的多次跳转,一定程度上提高了系统的复杂性,但是因为三者之间职责的清晰,在一定程度上来说是可以接受的。

开闭原则

开闭原则由两点组成。

  • 开:对拓展是开放。
  • 闭:对修改是关闭。

一个软件实体的任何变更,不应该是建立在修改的基础之上,我们应该是对软件实体进行拓展来达到变更需求的目的的。

其中的变化可以是需求、类及方法,上线的产品必然是经过大量的测试验证的,是一种趋于稳定的状态,如果把原来的东西修修改改,必然是要做大量回归测试,不仅耗时耗力,而且修改的多了,你也不能保证每次修改都可以兼容上次修改。

开闭原则是一个抽象的概念,上述五大原则和设计模式就是对于开闭原则进行的实际落实的成果,而其中实现开闭原则的最核心的思想就是抽象,把一切可能的变化都抽象出来。那么不管怎么变都可以对变化进行扩展。所以我们需要遵守依赖倒置原则,必须依赖接口而不是细节。
最重要的就是我们不应该修改,而是创建

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值