设计模式-六大原则

单一职责原则

有且只有一个原因会引起类的变化,即是说,一个类只会对一个职责内的事情负责。比如说权限类,那么他只负责权限内的所有事情,其他登录或者角色等相关的一些东西他不会干涉,也不要把权限的事情放到其他的类里面。一定要记住,手不要伸太长,否则的话,相关的代码分散到系统各个地方,维护的代码是在太大,随着你的系统的扩张,维护的代码也会呈几何增长。

想象一下中医药方,每个抽屉值只放了一种药材,抓方子的时候很方便,直接刷起来称重就可以了,如果是多种药材放到一起的话,画面太美不敢想象。

 

接口隔离原则:

接口的定义:

1.实例接口:其实就是java当中的类,而你的具体事例必须遵守类,也就是调用方法,属性这些东西。你A类不能总不能去调用B类的方法和属性变量吧?

2.类接口:也就是java中的interface关键字定义的接口。

隔离的定义:

1.客户端不应该依赖它不需要的接口。

2.类间的关系应该建立在最小的接口上。

两句话汇总起来就是:对于类接口来说客户端应该只需要它需要的接口,这个接口只做了客户端期望的事情,也就是说接口必须要保持小而纯净,避免臃肿。而对于实际接口来说,一个接口中的方法必须劲量的少。这边和单一职责的区别就是单一职责原则是要让类和接口的职责单一,注重的是职责,是业务上的划分,而接口隔离指的是接口职责来说的,也就是你的接口职责必须单一(接口只做自己该做的事情,需要小而纯净)。

举个例子,你去吃饭,看到有收银员,厨师,服务员,洗碗阿姨等等,类比接口隔离,这些人就是一个一个的接口,而他们只作自己负责的事情就是保持了接口的纯净。收营员只收钱而不会去洗碗。

他们各司其职,而你需要干什么事情了只会去找对应的人,你不会愿意收营员收完钱之后去洗碗,对于你来说可能无所谓,但是收营员去收钱了,那下一个人还怎么付钱???下一个人只会愿意收营员收钱。所以,收营员接口的收银方法必须只干收银的事情。

那么我们如何保证接口的纯净呢?

1.接口要尽量的小:

这是接口隔离原则的核心定义,不要出现臃肿的接口,但是小也是有限度的,细化接口可以帮助我们将代码逻辑隔离,但是同时也带来了接口泛滥导致难以维护的代价。

2.接口要高内聚:

高内聚就是要提高类,接口,模块的处理能力,也就是尽量减少对外公开方法,当你对外方法越少,变更的风险就越少。

3.定制服务:

定制服务就是单独为一个个体提供优良的服务,类比上面的例子就是收营员可能对你是收银,退款服务,而对于他的老板来说就是报账,核账服务了。因为不同的个体的需求是不同的,就算目前一样,但是对于以后来说你能保证永远一样么?

4.接口的设计是又限度的:

接口的设计师有限度的,还是那句话,一切都要结合实际情况来定。任何事情都是有一个度的,大家牢记过犹不及。。

 

里氏替换原则

里氏替换原则建立在继承之上,牢牢记住         A extends B   ,B可以是实现类或者是抽象类。

目的是为了让子类更好的复用自父类的继承而来的方法。通俗说在任何父类的地方都可以被替换成子类,并且程序不会有报错和出错。有四条规则规定了继承之间的限制:

1.子类必须完全实现父类的方法。

2.子类可以有自己的个性。

3.覆盖或实现父类的方法时输入参数可以被方法。

4.覆盖或实现父类的方法时返回值可以被缩小。

先说说第一条,调用实例方法必须是可以被new的,因此A中必然是实现了B的抽象方法,第二条,子类当然可以有自己独有的方法,难点在于第三条,因此在父类的地方可以被无痕的替换为子类,因此子类最好不要去覆盖父类中的方法,如果真的要覆盖的话那么,子类的入参就必须父类入参的父类。既child.method(Map map),father.method(HashMap map);

在调用真正调用的时候,你传入的是HashMap的实例,就重载调用父类的method方法,而不会去调用子类的method方法,这样程序原本的规则就没有任何的变化。举个例子。

public class Father {
    public Collection doSomething(HashMap map){
        System.out.println("父类被执行了...");
        return map.values();
    }
}
public class Son extends Father {

    //方法参数类型
    public Collection doSomething(Map map) {
        System.out.println("子类被执行了...");
        return map.values();
    }
}
public class Test {
    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);

    }
}

输出

父类被执行了...
父类被执行了...
main方法中,Son对象完美的替换了Father对象,调用了父类的方法。

而对于第四条,子类的覆盖方法的返回值必须是父类方法的子类。

 

依赖倒置原则:

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

什么是高层模块,底层模块?

底层原子性的操作就是底层模块,以底层模块为基础构建新的模块的就是高层模块

抽象和细节?

抽象就是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),就是说一个对象应当对其他对象有尽可能少的了解,不和陌生人说话。英文简写为: LoD.

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

关于朋友的定义:

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

简单的说不要过多的去调用在方法中定义的类的方法,反过来,类不要过多的暴露方法给外部,暴露的越多,类之间的耦合度就越高,当类改动的越大,风险就越大。距离产生美,过多的亲密接触(方法之间的调用)最后任何一方的改动就会影响到另外一方,你就越痛苦。


/**
 * 明星
 *
 * Created by Administrator on 2018/7/24 0024.
 */
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("大明星去演出了。");
    }

}
/**
 * 经纪人
 *
 * Created by Administrator on 2018/7/24 0024.
 */
public class Broker {
    public void reservation(Merchant merchant){
        System.out.println("经纪人预约。");
        merchant.business();
    }
}
/**
 * 商家
 * Created by Administrator on 2018/7/24 0024.
 */
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("慈善");
    }
}

输出:

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

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


/**
 * 明星
 *
 * Created by Administrator on 2018/7/24 0024.
 */
public class Star {

    private Broker broker;

    public Star() {
    }

    public Star(Broker broker) {
        this.broker = broker;
    }

    public Broker getBroker() {
        return broker;
    }

    public void setBroker(Broker broker) {
        this.broker = broker;
    }

    public void play(){
        //直接和经纪人说去洽谈演出去,和谁接触我不管。
        broker.reservation();
        System.out.println("大明星去演出了。");
    }

}
/**
 * 经纪人
 *
 * Created by Administrator on 2018/7/24 0024.
 */
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();
    }
}
/**
 * 商家
 * Created by Administrator on 2018/7/24 0024.
 */
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();
        star.setBroker(broker);
        star.play();
    }
}

输出:

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

大明星不和商家接触,只和关系亲密的经纪人接触就可以了,好处就是当活动有任何的变更时,影响范围只会波及到经纪人,并不会对明星有任何的影响,明星哪能事事都关注不是,明星和商家之间就没有耦合关系了。

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

 

开闭原则:

开闭原则由开和闭两点组成

开:对扩展是开放。

闭:对修改是关闭。

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

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

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

需求变化是永无止境的,并且是没有边界的,他会出现在任何地方,所以,我们需要接口隔离,把接口方法缩小。

总之,最重要的只需要记得我们不应该修改,而是创建。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值