设计模式之禅学习笔记

六个原则

1、单一指责原则

定义: 应该有且仅有一个原因引起类的变更;

也就是说一个类或者一个接口只有一个职责,它只负责一件事情;单一职责原则提出了一个编写程序的标准, 用 “职责”或“变化原因” 来衡量接口或类设计得是否优良,但是 “职责”和“变化原因” 都是不可度量的, 因项目而异, 因环境而异。

对于接口,我们设计的时候一定要保证职责的单一;但是对于实现类就需要多方面考虑了。生搬硬套单一职责原则会引起类的剧增,给维护带来非常多的麻烦,而且过分细分类的职责也会人为地增加系统的复杂性。

单一职责不仅适合接口和类,也适用于方法;一个方法尽可能只做一件事情;

对于单一职责原则,建议接口一定要做到单一职责,类的设计尽量做到只有一个原因引起变化。

2、里氏替换原则(LSP)

定义: 所有引用基类的地方必须能透明地使用其子类的对象

在java中,即在父类出现的地方子类都可以出现,而且替换为子类不会出现任何异常和错误; 但是反过来,有子类出现的地方,父类就未必能出现,所以向下转型来调用方法的时候,会出现异常

1、子类必须实现父类中的方法

我们在做系统设计时,经常会定义一个接口或抽象类,然后编码实现,调用类则直接传入接口或抽象类;

注意: 在类中调用其他类时务必要使用父类或接口, 如果不能使用父类或接口, 则说明 类的设计已经违背了LSP原则

注意: 如果子类不能完整地实现父类的方法, 或者父类的某些方法在子类中已经发 生“畸变”, 则建议断开父子继承关系, 采用依赖、 聚集、 组合等关系代替继承。

2、子类可以有自己的个性

//父类Rilfe
public class Rilfe extends AbstractGun {
    @Override
    void shoot() {
        System.out.println("RILFE 射击....");
    }
}
//子类
public class AKG extends Rilfe {
    @Override
    void shoot() {
        System.out.println("AKG47 射击....");
    }
}

public class Client {
    public static void main(String[] args) {
        Solider solider = new Solider();
        //正常运行
        solider.setAbstractGun(new AKG());
        solider.killEnermy();
        //将抛出ClassCastException以常
        solider.setAbstractGun((AKG) new Rilfe());
        solider.killEnermy();
    }
}
复制代码

因为子类可以有自己的个性,所以向下转型是不安全的,也即有子类出现的地方,父类不一定可以出现

3、覆盖或实现父类的方法时输入参数可以被放大

这里定义一个父类和子类

//父类
public class Father {
    public Collection doSomething(HashMap hashMap){
        System.out.println("父类被执行");
        return hashMap.values();
    }
}

//子类
public class Son extends Father {
    // **注意** 这里没有使用@Override注解
    public Collection doSomething(Map map) {
        return map.values();
    }
}

public class Client {
    //父类执行
    public static void invoke(){
        Father father = new Father();
        HashMap map = new HashMap();
        father.doSomething(map);
    }
    //子类调用
    public static void invoke1(){
        Son son = new Son();
        HashMap map = new HashMap();
        son.doSomething(map);
    }

    public static void main(String[] args) {
        invoke();//输出:父类被执行
        invoke1();//输出:父类被执行
    }
}
复制代码

方法名相同,但是参数范围不同就不是重写,加上@Override注解就会报错,因为这是重载; 这种重载的情况,必须子类的参数范围比父类大,才不违背里氏原则,父类出现的地方,子类一定可以出现;如果要调用子类的方法,就要重写父类的方法; 子类中方法的前置条件必须与超类中被覆写的方法的前置条件相同或者更宽松

4. 覆写或实现父类的方法时输出结果可以被缩小

父类的一个方法的返回值是一个类型T,子类的相同方法(重载或覆写)的返回值为S,那么里氏替换原则就要求S必须小于等于T,也就是说,要么S和T是同一个类型, 要么S是T的子类,为什么呢? 分两种情况,如果是覆写,父类和子类的同名方法的输入参数是相同的,两个方法的范围值S小于等于T,这是覆写的要求,这才是重中之重,子类覆写父类的方法,天经地义。如果是重载,则要求方法的输入参数类型或数量不相同,在里氏替换原则要求下,就是子类的输入参数宽于或等于父类的输入参数,也就是说你写的这个方法是不会被调用的;

在项目中, 采用里氏替换原则时,尽量避免子类的“个性”,一旦子类有“个性”,这个子类和父类之间的关系就很难调和了,把子类当做父类使用,子类的“个性”被抹杀——委屈了点;把子类单独作为一个业务来使用, 则会让代码间的耦合关系变得扑朔迷离——缺乏类替换的标准

3、依赖倒置原则

含义:

  • 高层模块不应该依赖低层模块, 两者都应该依赖其抽象
  • 抽象不应该依赖细节
  • 细节应该依赖抽象

低层模块: 每一个逻辑的实现都是由原子逻辑组成的,不可分割的原子逻辑就是低层模块

高层模块: 原子逻辑的再组装就是高层模块

抽象: 抽象类或者接口

细节: 具体的实现类

翻译成java表述为:

  • 模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的;
  • 接口或抽象类不依赖于实现类
  • 实现类依赖接口或抽象类

面向接口编程

4、接口隔离原则

接口:

  • 实例接口:java中的类也是一个接口
  • 类接口:interface关键字定义的接口

隔离:

  • 客户端不应该依赖它不需要的接口
  • 类间的依赖关系应该建立在最小的接口上;要去接口细化,接口纯洁

5、迪米特法则

定义: 一个对象应该对其他对象有最少的了解。

6、开闭原则

定义: 一个软件实体如类、 模块和函数应该对扩展开放, 对修改关闭

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值