Java中的设计模式

发布于:2025-05-16 ⋅ 阅读:(15) ⋅ 点赞:(0)

一、工厂方法模式

1.1 简单工厂模式

1.1.1 案例引入

比方说我们要设计一个披萨店的点单程序,披萨店有水果披萨,有芝士披萨,两种类型,选择哪个披萨,只需要创建那个类型的披萨对象即可。

package org.example;

import org.example.pizza.CheesePizza;
import org.example.pizza.FruitPizza;
import org.example.pizza.Pizza;

public class Main {
    public static void main(String[] args) {
        orderPizza("水果");
    }

    public static void orderPizza(String type) {
        Pizza pizza = null;
        if ("芝士".equals(type)) {
            pizza = new CheesePizza();
        } else if ("水果".equals(type)) {
            pizza = new FruitPizza();
        } else {
            throw new RuntimeException("不支持的披萨类型");
        }
        pizza.prepare();
    }
}
package org.example.pizza;

public interface Pizza {

    void prepare();
}
package org.example.pizza;

public class CheesePizza implements Pizza {
    @Override
    public void prepare() {
        System.out.println("准备芝士");
    }
}
package org.example.pizza;

public class FruitPizza implements Pizza {
    @Override
    public void prepare() {
        System.out.println("准备水果");
    }
}

这里我们发现,这段代码的拓展非常之困难,每次店内增加类型的披萨,都需要改这段代码,耦合性太强。也违背了开闭原则:对扩展开放,对修改关闭。即要拓展新业务的时候,不能修改旧的业务。

if ("芝士".equals(type)) {
	pizza = new CheesePizza();
} else if ("水果".equals(type)) {
	pizza = new FruitPizza();
}

1.1.2 工厂模式

工厂模式的核心思想:外包生产。创建一个工厂类,工厂中根据type来制作披萨。这样主函数只需要传递类型,不需要创建对象,让工厂去自己判断类型,创建对象。

package org.example.pizza;

public class PizzaFactory {

    public Pizza createPizza(String type) {
        if ("芝士".equals(type)) {
            return new CheesePizza();
        } else if ("水果".equals(type)) {
            return new FruitPizza();
        } else {
            throw new RuntimeException("不支持的披萨类型");
        }
    }
}
package org.example;

import org.example.pizza.Pizza;
import org.example.pizza.PizzaFactory;

public class Main {
    public static void main(String[] args) {
        PizzaFactory pizzaFactory = new PizzaFactory();
        Pizza cheesePizza = pizzaFactory.createPizza("芝士");
        cheesePizza.prepare();
    }
}
  • 我们发现,我们点单不用创建对象了,而是和工厂打交道,告诉工厂我需要什么类型的对象,工厂中再去创建对象。解耦了我们和披萨对象之间的关系。
  • 缺点:我们发现,如果想要增加披萨的种类,是要在PizzaFactory中的createPizza方法中增加if语句,这就违反了开闭原则,开闭原则是对拓展开放,对修改关闭。但增加if语句就是对修改也开放了,所以简单工厂模式仍不是最好的方案。

1.1.3 工厂模式的优点

如果我们使用工厂来生产对象,我们就只和工厂打交道就可以了,彻底和对象解耦,如果要更换对象,直接在工厂里更换该对象即可,达到了与对象解耦的目的;所以说,工厂模式最大的优点就是:解耦。

1.2 工厂方法模式

工厂方法模式,简而言之,就是给上面工厂类也做成接口,让下面的披萨工厂实现接口,那么当新的披萨种类来的时候,只需要创建一个新类,实现披萨工厂接口即可。完全不会改动原来代码。

1.2.1 角色

工厂方法模式的主要角色:

  1. 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品。
  2. 具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
  3. 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
  4. 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。

1.2.2 案例说明

仍然是披萨问题,此时我们给工厂也改成接口,并分别有两个实现类。

package org.example.pizza.factory;

import org.example.pizza.pizza.Pizza;

public abstract interface PizzaFactory {

    Pizza createPizza();
}

package org.example.pizza.factory;

import org.example.pizza.pizza.CheesePizza;
import org.example.pizza.pizza.Pizza;

public class CheesePizzaFactory implements PizzaFactory {

    @Override
    public Pizza createPizza() {
        return new CheesePizza();
    }
}

package org.example.pizza.factory;

import org.example.pizza.pizza.FruitPizza;
import org.example.pizza.pizza.Pizza;

public class FruitPizzaFactory implements PizzaFactory {

    @Override
    public Pizza createPizza() {
        return new FruitPizza();
    }
}

还有一个披萨店类,店中有一个点单的方法,方法中去创建披萨,准备披萨。

package org.example.pizza;

import lombok.Data;
import org.example.pizza.factory.PizzaFactory;
import org.example.pizza.pizza.Pizza;

@Data
public class PizzaStore {

    private PizzaFactory pizzaFactory;

    public Pizza orderPizza() {
        Pizza pizza = pizzaFactory.createPizza();
        pizza.prepare();
        return pizza;
    }
}

主函数模拟用户操作。直接创建的是一个披萨店类,并给他设置披萨类型工厂属性,再调用点单接口,就会自动地创建披萨等操作。

package org.example;

import org.example.pizza.factory.CheesePizzaFactory;
import org.example.pizza.PizzaStore;
import org.example.pizza.pizza.Pizza;

public class Main {

    public static void main(String[] args) {
        PizzaStore pizzaStore = new PizzaStore();
        pizzaStore.setPizzaFactory(new CheesePizzaFactory());
        Pizza pizza = pizzaStore.orderPizza();
        System.out.println(pizza.getName());
    }
}
  • 从以上的编写的代码可以看到,要增加产品类时也要相应地增加工厂类,不需要修改工厂类的代码了,这样就解决了简单工厂模式的缺点。
  • 工厂方法模式是简单工厂模式的进一步抽象。由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。
  • 缺点:每增加一个披萨类型,就需要新增一个工厂类实现工厂接口;无疑增加了代码复杂度,但满足了开闭原则,缺点也可以忽略。

1.3 抽象工厂模式

1.3.1 什么是抽象工厂模式

  • 工厂方法模式,只考虑生产同等级的产品,例如这个工厂都生产披萨。
  • 而想象你有一个总部工厂,这个工厂不直接生产披萨,而是生产一整套相关产品,叫做产品族(披萨 + 酱料 + 盒子)。每个地区分厂继承总部的规范,自行实现全套产品。
  • 抽象工厂是一个超级工厂,他能创建其他工厂。

1.3.2 三大特征

  • 产品族:生产一组相关对象(披萨、酱料、盒子)。
  • 约束性:不同分厂的产品必须配套使用(比如纽约不能用芝加哥的厚酱料。
  • 易扩展:新增地区(如东京分厂)时,只需实现抽象工厂接口,无需修改已有代码。

二、策略模式

2.1 案例引入

我们去旅游选择出行模式有很多种,可以骑自行车、可以坐汽车、可以坐火车、可以坐飞机。

在这里插入图片描述

2.2 策略模式

2.2.1 策略模式定义

该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。

2.2.2 策略模式角色

在这里插入图片描述

  • 抽象策略(Strategy)类:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
  • 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现或行为。
  • 环境(Context)类:持有一个策略类的引用,最终给客户端调用。

三、责任链模式

3.1 案例引入

在现实生活中,常常会出现这样的事例:一个请求有多个对象可以处理,但每个对象的处理条件或权限不同。例如,公司员工请假,可批假的领导有部门负责人、副总经理、总经理等,但每个领导能批准的天数不同,员工必须根据自己要请假的天数去找不同的领导签名,也就是说员工必须记住每个领导的姓名、电话和地址等信息,这增加了难度。

3.2 责任链模式

3.2.1 定义

  • 又名职责链模式,为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
  • 比较常见的springmvc中的拦截器,web开发中的filter过滤器。
    在这里插入图片描述

3.2.2 角色

  • 抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
  • 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
  • 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。

3.2.3 代码实现

背景:一个订单需要经历如下步骤,1是校验订单,2是计算金额,3是入库。我们可以使用责任链模式,顺序完成任务。

首先创建一个Handler,他有一个抽象方法,还有一个next属性,用来设置下一个Handler是谁。

package org.example.handle;

public abstract class Handler {

    public Handler handler;

    public void setNext(Handler handler) {
        this.handler =  handler;
    }

    public abstract void process(OrderInfo orderInfo);
}

校验订单。实现了Handler,并执行自己方法,执行完后通过process执行下一个Handler。

package org.example.handle;

public class OrderValidate extends Handler {

    @Override
    public void process(OrderInfo orderInfo) {
        System.out.println("校验订单基本信息");
        handler.process(orderInfo);
    }
}

计算金额。同上。

package org.example.handle;

public class OrderFill extends Handler {
    @Override
    public void process(OrderInfo orderInfo) {
        System.out.println("补充订单信息");
        handler.process(orderInfo);
    }
}

入库。因为是最后一步,所以不需要process了。

package org.example.handle;

public class OrderCreate extends Handler {
    @Override
    public void process(OrderInfo orderInfo) {
        System.out.println("入库");
    }
}

Main方法。首先创建出三个类型的Handler。再设置责任链路,比如校验订单的next是计算金额,计算金额下一步是入库。并最后开始从校验订单开始执行。

package org.example.handle;

public class Main {
    public static void main(String[] args) {
        Handler orderValidate = new OrderValidate();
        Handler orderFill = new OrderFill();
        Handler orderCreate = new OrderCreate();
        //设置责任链路
        orderValidate.setNext(orderFill);
        orderFill.setNext(orderCreate);
        //开始执行
        orderValidate.process(new OrderInfo());
    }
}

在这里插入图片描述

3.2.3 优缺点

优点

  • 降低了对象之间的耦合度。
  • 该模式降低了请求发送者和接收者的耦合度。
  • 增强了系统的可扩展性。
  • 可以根据需要增加新的请求处理类,满足开闭原则。
  • 增强了给对象指派职责的灵活性。
  • 当工作流程发生变化,可以动态地改变链内的成员或者修改它们的次序,也可动态地新增或者删除责任。
  • 责任链简化了对象之间的连接。
  • 一个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
  • 责任分担。
  • 每个类只需要处理自己该处理的工作,不能处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。

缺点

  • 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
  • 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。

网站公告

今日签到

点亮在社区的每一天
去签到