设计模式

Java设计模式简介

  • 设计模式(Design pattern):是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。(23中设计模式别称:GOF23)
    • 模式:在某些场景下,针对某类问题的某种通用的解决方案。
    • 场景:项目所在的环境
    • 问题:约束条件,项目目标等
    • 解决方案:通用、可复用的设计,解决约束达到目标。
  • 设计模式的三个分类:
    • 创建型模式:对象实例化的模式,创建型模式用于解耦对象的实例化过程。
      • 五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
    • 结构型模式:把类或对象结合在一起形成一个更大的结构。
      • 适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
    • 行为型模式:类和对象如何交互,及划分责任和算法。
      • 策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
  • 7大设计模式原则:
设计原则 设计原则定义 设计原则详解
开闭原则 开闭原则是指一个软件实体,如类、模块和函数应该对扩展开放,对修改关闭,也就是说一个软件实体应该通过扩展来实现变化,而不是通过修改已有的代码来实现变化。 开闭原则详解
里氏替换原则 里氏替换原则是关于继承的一个原则,遵循里氏替换原则能够更好地发挥继承的作用,里氏替换原则最早是在1988年,由麻省理工学院的一位姓里的女士(Barbara Liskov)提出来的。子类可以扩展父类的功能,但不能改变父类的功能 里氏替换原则详解
迪米特原则 迪米特原则它要求一个对象应该对其他对象有最少的了解,所以迪米特法则又叫做最少知识原则。 迪米特原则详解
单一职责原则 单一职责原则(Single Responsibility Principle)是面向对象设计原则的一种。单一职责原则是指不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。 单一职责原则详解
接口分离原则 接口分离原则指在设计时采用多个与特定客户类有关的接口比采用一个通用的接口要好。 接口分离原则详解
依赖倒置原则 依赖倒置原则指的是高层模块(稳定)不应该依赖于低层模块(变化),二者都应该依赖于抽象(稳定)。抽象(稳定)不应该依赖于实现细节(变化),实现细节应该依赖于抽象(稳定)。即面向接口编程 依赖倒置原则
组合/聚合复用原则 组合/聚合复用原则是指尽量使用组合/聚合,不要使用类继承。 组合/聚合复用原则详解
  • 23种设计模式简述:
    1. 单例模式:某个类只能有一个实例,提供一个全局的访问点。
    2. 工厂方法模式:工厂方法模式是简单工厂模式的进一步抽象和推广,是GoF设计模式的一种。由于使用了面向对象的多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。
    3. 抽象工厂模式:创建相关或依赖对象的家族,而无需明确指定具体类。
    4. 建造者模式:封装一个复杂对象的构建过程,并可以按步骤构造。
    5. 原型模式:通过复制现有的实例来创建新的实例。
    6. 适配器模式:将一个类的方法接口转换成客户希望的另外一个接口。
    7. 组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构。
    8. 装饰模式:动态的给对象添加新的功能。
    9. 代理模式:为其他对象提供一个代理以便控制这个对象的访问。
    10. 享元(蝇量)模式:通过共享技术来有效的支持大量细粒度的对象。
    11. 外观模式:对外提供一个统一的方法,来访问子系统中的一群接口。
    12. 桥接模式:将抽象部分和它的实现部分分离,使它们都可以独立的变化。
    13. 模板模式:定义一个算法结构,而将一些步骤延迟到子类实现。
    14. 解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器。
    15. 策略模式:定义一系列算法,把他们封装起来,并且使它们可以相互替换。
    16. 状态模式:允许一个对象在其对象内部状态改变时改变它的行为。
    17. 观察者模式:对象间的一对多的依赖关系。
    18. 备忘录模式:在不破坏封装的前提下,保持对象的内部状态。
    19. 中介者模式:用一个中介对象来封装一系列的对象交互。
    20. 命令模式:将命令请求封装为一个对象,使得可以用不同的请求来进行参数化。
    21. 访问者模式:在不改变数据结构的前提下,增加作用于一组对象元素的新功能。
    22. 责任链模式:将请求的发送者和接收者解耦,使的多个对象都有处理这个请求的机会。
    23. 迭代器模式:一种遍历访问聚合对象中各个元素的方法,不暴露该对象的内部结构。

代理模式

  • 代理模式(Proxy Pattern) 是一种结构型设计模式, 让你能够提供对象的替代品或其占位符。 代理控制着对于原对象的访问,并允许在将请求提交给对象前后进行一些处理。代理模式给某一个对象提供一个代理,并由代理对象控制对原对象的引用。代理模式的英文叫做Proxy或Surrogate,它是一种对象结构型模式。。
  • 三种代理模式:
    1. 静态代理;
    2. JDK动态代理;
    3. Cglib动态代理。

静态代理

工厂设计模式

  • 属于创建者模式;
  • 实现了创建者和调用者的分离;
  • 分类:
    1. 简单工厂模式;
    2. 工厂方法模式;
    3. 抽象工厂模式;
  • 主要遵循原则:
    1. 开闭原则;
    2. 依赖倒置原则;
    3. 迪米特原则;
  • 核心本质:
    1. 实例化对象不使用new,用工厂方法替代;
    2. 将选择实现类,创建对象统一管理和控制,从而将调用者和我们的实现类解耦。

简单工厂模式

  • 用来生产同一等级结构中的任意产品(对于增加新的产品,需要扩展已有的代码)
  • 虽然不符合开闭原则,但是因为其简单,所以实际中使用得最多。
  • 示例:

//车接口
package simpliFactory;


public interface Car {

    void name();
}

//benci车实体类
package simpliFactory;

public class BenciCar implements Car {

    @Override
    public void name() {
        System.out.println("benci");
    }
}

//bmw车实体类
package simpliFactory;

public class BMWCar implements Car {

    @Override
    public void name() {
        System.out.println("BMW");
    }
}

//车工厂
package simpliFactory;

public class CarFactory {
    public static Car getCar(String name) {
      //想要增加新的车型,必须修改这里面的代码
        if(name.equals("bmw")) {
            return new BMWCar();
        }else if(name.equals("benci")) {
            return new BenciCar();
        }else {
            return null;
        }
    }
}

//消费者从车工厂购买车
package simpliFactory;

public class Consumer {
    public static void main(String[] args) {
        Car car1 = CarFactory.getCar("bmw");
        Car car2 = CarFactory.getCar("benci");

        car1.name();
        car2.name();
    }
}

工厂方法模式

  • 用来生产同一等级结构中的固定产品(支持增加任意产品)
  • 不修改已有类的前提下,通过增加新的工厂类来实现扩展。
  • 示例:

//车接口
package methodFactory;

public interface Car {

    void name();
}
//车工厂接口
package methodFactory;

public interface CarFactory {
     Car getCar();
}
//BMW车实体类
package methodFactory;

public class BMWCar implements Car {

    @Override
    public void name() {
        System.out.println("BMW");
    }
}
//Benci车实体类
package methodFactory;

public class BenciCar implements Car {

    @Override
    public void name() {
        System.out.println("benci");
    }
}

//BMW车工厂类
package methodFactory;

public class BMWFactory implements CarFactory {
    @Override
    public  Car getCar() {
        return new BMWCar();
    }
}

//Benci车工厂类
package methodFactory;

public class BenciFactory implements CarFactory{
    @Override
    public Car getCar() {
        return new BenciCar();
    }
}

//消费者从对应的工厂获取对应的车
package methodFactory;

public class Consumer {
    public static void main(String[] args) {
        Car car1 = new BMWFactory().getCar();
        Car car2 = new BenciFactory().getCar();

        car1.name();
        car2.name();
    }
}

抽象工厂模式

  • 抽象工厂提供了一个创建一系列相关或相互依赖产品(即一个产品簇)的接口,无需指定它们具体的类。
  • 核心:将一个系列的产品统一到一起创建。增加新系列的产品容易,但是修改产品簇中的产品困难。
  • 示例:
//产品簇中产品之一-手机接口
package abstractFactory;

public interface IphoneProduct {

    void call();
}

//产品簇中产品之一-路由器接口
package abstractFactory;

public interface IRouterProduct {

    void openWifi();
}


//小米系列产品之手机实体类
package abstractFactory;

public class XIaomiPhone  implements IphoneProduct{
    @Override
    public void call() {
        System.out.println("小米打电话");
    }
}

//小米系列产品之路由器实体类
package abstractFactory;

public class XIaomiRouter implements IRouterProduct {
    @Override
    public void openWifi() {
        System.out.println("小米路由器连wifi");
    }
}

//华为系列产品之手机实体类
package abstractFactory;

public class HuaweiPhone  implements IphoneProduct{
    @Override
    public void call() {
        System.out.println("华为打电话");
    }
}

//华为系列产品之路由器实体类
package abstractFactory;

public class HuaweiRouter implements IRouterProduct{
    @Override
    public void openWifi() {
        System.out.println("华为路由器连wifi");
    }
}

//抽象工厂-用于生产生产不同系列产品的工厂
package abstractFactory;

public interface IProductFactory {

    IphoneProduct producePhone();
    IRouterProduct produceRouter();
}

//小米系列产品的生产工厂
package abstractFactory;

public class XiaomiProductFactory implements IProductFactory {
    @Override
    public IphoneProduct producePhone() {
        return new XIaomiPhone();
    }

    @Override
    public IRouterProduct produceRouter() {
        return new XIaomiRouter();
    }
}

//华为系列产品的生产工厂
package abstractFactory;

public class HuaweiProductFactory implements IProductFactory {
    @Override
    public IphoneProduct producePhone() {
        return new HuaweiPhone();
    }

    @Override
    public IRouterProduct produceRouter() {
        return new HuaweiRouter();
    }
}


//从不同工厂取出不同系列产品
package abstractFactory;

public class Client {

    public static void main(String[] args) {
        IProductFactory xiaomi = new XiaomiProductFactory();
        IphoneProduct phone1 = xiaomi.producePhone();
        IRouterProduct router1 = xiaomi.produceRouter();

        IProductFactory huawei = new HuaweiProductFactory();
        IphoneProduct phone2 = huawei.producePhone();
        IRouterProduct router2 = huawei.produceRouter();

        phone1.call();
        router1.openWifi();

        phone2.call();
        router2.openWifi();
    }
}

代理模式

静态代理

  • 抽象角色:一般用接口或抽象类来解决,用来统一真实角色和代理角色的核心功能。
  • 真实角色:被代理的角色;
  • 代理角色:代理真实角色,代理真实角色后,一般会增加一些附属功能。
  • 客户:访问代理对象的人。

  • 优点:
    • 可以使真实角色的业务简化,不用去关注一些公共的业务;
    • 公共业务交给代理角色,实现了业务的分工;
    • 公共业务发生扩展的时候,方便集中管理,不用去直接修改原有代码,只用增强代理角色的功能。
  • 缺点:
    • 一个真实角色就会产生一个代理角色,代码量会翻倍。
  • 代码实现步骤:
    1. 接口:
package staticProxy;

//真实角色和代理角色都要实现相同的接口,以确保代理和真实角色的核心功能统一,例如,房东不需要婚介所
public interface Rent {
    void rent();
}
  1. 真实角色:
package staticProxy;

//房东-真实角色
public class Host implements Rent{
    @Override
    public void rent() {
        System.out.println("房东出租房屋");
    }
}
3. 代理角色:
package staticProxy;

//中介-代理角色
public class Proxy implements Rent {

    private Host host;

    public Proxy(Host host){
        this.host = host;
    }

    //代理角色除了实现真实角色的功能外,往往还需实现一些附属功能,这正是代理的作用所在。
    @Override
    public void rent() { //核心功能rent中添加一些附属功能
        seeHouse();
        host.rent();
        fare();
    }

    public void seeHouse() {
        System.out.println("中介带客户看房子");
    }

    public void fare() {
        System.out.println("中介收取费用");
    }
}
4. 客户:
package staticProxy;

public class Client {

    public static void main(String[] args) {
        Host host = new Host();
        Proxy proxy = new Proxy(host);
        proxy.rent();
    }
}

动态代理

  • 动态代理和静态代理发挥的作用相同,只是代理角色生成的方式不同;
  • 动态代理的代理类是动态生成的,不是像静态代理类一样是我们事先写好的;
  • 动态代理的分类:
    1. 基于接口的动态代理;
    2. 基于类的动态代理;
  • 动态代理的实现方式:
    1. jdk动态代理–基于接口;
    2. cglib–基于类;
    3. javasist–java字节码实现;

jdk动态代理

  • jdk的动态代理是通过Proxy类和 InvocationHandler接口实现的;
  • Proxy类提供生成满足对应接口的代理实例的静态方法;
  • InvocationHandler是一个接口,每一个动态生成的代理实例都有一个对应的实现了InvocationHandler接口的类。
  • 示例:
//统一真实角色和代理角色的接口
public interface Rent {
    void rent();
}

//真实角色1
public class Host implements Rent {
    @Override
    public void rent() {
        System.out.println("房东1出租房子");
    }
}

//真实角色2
public class Host2 implements Rent {
    @Override
    public void rent() {
        System.out.println("房东2出租房屋");
    }
}

//代理处理类,用于生成实现和真实角色一样接口的代理实例
public class ProxyInvocationHandler implements InvocationHandler {

    //需要实现的接口
    private Object target;

    //设置对应的接口
    public void setTarget(Object target){
        this.target = target;
    }

    //获取代理实例
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }

    //实现代理实例的方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(target,args);
        log(method.getName());
        return result;
    }

    //添加代理实例的附属方法
    public void log(String methodName) {
        System.out.println("执行了"+methodName+"方法");
    }
}

//通过代理实现租房
public class Client {
    public static void main(String[] args) {
        Host host1 = new Host();
        Host2 host2 = new Host2();

        ProxyInvocationHandler pih = new ProxyInvocationHandler();//生成代理处理对象
        pih.setTarget(host1);//设置需要被代理的真实对象
        Rent proxy = (Rent)pih.getProxy();//生成对应的代理对象
        proxy.rent();

        pih.setTarget(host2);
        Rent proxy2 = (Rent) pih.getProxy();
        proxy2.rent();
    }
}