模板方法
模板方法模式,它在一个抽象类中定义了一个算法(业务逻辑)的骨架,具体步骤的实现由子类提供,它通过将算法的不变部分放在抽象类中,可变部分放在子类中,达到代码复用和扩展的目的。
- 复用: 所有子类可以直接复用父类提供的模板方法,即上面提到的不变的部分。
- 扩展: 子类可以通过模板定义的一些扩展点就行不同的定制化实现。
我们来看一下示例代码就很清晰了
abstract class AbstractClass { //模板类
// 模板方法
public final void templateMethod() {
primitiveOperation1();
primitiveOperation2();
hook();
}
// 基本操作(抽象方法)
protected abstract void primitiveOperation1();
protected abstract void primitiveOperation2();
// 钩子方法(可选的操作,提供默认实现)
protected void hook() {}
}
class ConcreteClassA extends AbstractClass { //具体方法A
@Override
protected void primitiveOperation1() {
System.out.println("ConcreteClassA: primitiveOperation1");
}
@Override
protected void primitiveOperation2() {
System.out.println("ConcreteClassA: primitiveOperation2");
}
}
class ConcreteClassB extends AbstractClass { //具体方法B
@Override
protected void primitiveOperation1() {
System.out.println("ConcreteClassB: primitiveOperation1");
}
@Override
protected void primitiveOperation2() {
System.out.println("ConcreteClassB: primitiveOperation2");
}
@Override
protected void hook() {
System.out.println("ConcreteClassB: hook");
}
}
场景
应用场景:
例如支付场景,需要有支付宝、银行卡、微信支付多种方式,可以定义支付的骨架,骨架里包括一些获取订单、验证密码、执行支付、发送短信的逻辑,像验证密码这些公共的就由骨架来提供,执行支付就由子类来独立实现。
除此之外,支付场景,可能也会涉及到工厂模式,不同的支付方式,利用工厂模式不同的支付对象,再调用支付对象实现不同的功能。
策略模式
策略模式是一种行为型设计模式,提供一个策略接口,并独立实现各种具体策略,允许在运行时动态选择具体策略,从而实现更加灵活的代码结构。该模式的中心不是如何实现算法,而是如何组织、调用这些算法,方便调用方在针对不同场景灵活切换不同的策略。
// 策略的定义
public interface Strategy {
void execute(User user);
}
//具体策略A
public class AStrategy implements Strategy{
void execute(User user) {
System.out.println("Executing Strategy A " + user.getName());
}
}
//具体策略B
public class BStrategy implements Strategy{
void execute(User user) {
System.out.println("Executing Strategy B " + user.getName());
}
}
// 策略集合的创建
public class StrategyFactory {
private static final Map<String, Strategy> strategies = new HashMap<>();
static {
strategies.put("A", new AStrategy());
strategies.put("B", new BStrategy());
}
public static Strategy getStrategy(OrderType type) {
return strategies.get(type);
}
}
// 策略的使用
public class xxService {
public void execute(User user) {
String type = user.getType();
Strategy strategy = StrategyFactory.getStrategy(type);
return strategy.execute(user);
}
}
可以看到,上述的代码通过使用策略模式,可以将不同的算法策略封装起来,并根据 user 的类型在运行时动态选择具体的算法策略,从而提高系统的灵活性和可扩展性。
可以用在例如多种支付方式的切换、不同排序算法的切换等多策略实现的场景。
责任链模式
责任链模式允许将多个对象连接成一条链,并且沿着这条链传递请求,让多个对象都有机会处理这个请求,请求会顺着链传递,直到某个对象处理它为止。
在很多场景都能看到责任链模式,比如日志的处理,不同级别不同输出。再比如 Spring 过滤器的 Chain 也是责任链模式。
下面是一个简单的日志处理示例代码。
- 日志处理器有三种类型: 控制台日志处理器(ConsoleLogger)、文件日志处理器(FileLogger)、错误日志处理器(ErrorLogger)。
- 日志处理器按照优先级进行处理,如果当前处理器不能处理,则将请求传递给下一个处理器。
// 责任链模式的抽象日志类
abstract class Logger {
public static int INFO = 1;
public static int DEBUG = 2;
public static int ERROR = 3;
protected int level;
protected Logger nextLogger; //日志中可以存下一个日志
public void setNextLogger(Logger nextLogger) {
this.nextLogger = nextLogger;
}
public void logMessage(int level, String message) {
if (this.level <= level) {
write(message);
}
if (nextLogger != null) {
nextLogger.logMessage(level, message);
}
}
protected abstract void write(String message);
}
// 具体处理器类:控制台日志处理器
class ConsoleLogger extends Logger {
public ConsoleLogger(int level) {
this.level = level;
}
@Override
protected void write(String message) {
System.out.println("Standard Console::Logger: " + message);
}
}
// 具体处理器类:文件日志处理器
class FileLogger extends Logger {
public FileLogger(int level) {
this.level = level;
}
@Override
protected void write(String message) {
System.out.println("File::Logger: " + message);
}
}
// 具体处理器类:错误日志处理器
class ErrorLogger extends Logger {
public ErrorLogger(int level) {
this.level = level;
}
@Override
protected void write(String message) {
System.out.println("Error Console::Logger: " + message);
}
}
// 客户端代码
public class ChainPatternDemo {
private static Logger getChainOfLoggers() { //获取日志责任链
Logger errorLogger = new ErrorLogger(Logger.ERROR);
Logger fileLogger = new FileLogger(Logger.DEBUG);
Logger consoleLogger = new ConsoleLogger(Logger.INFO);
errorLogger.setNextLogger(fileLogger); //存下一个日志
fileLogger.setNextLogger(consoleLogger); //存再下一个日志
return errorLogger; //最后返回
}
public static void main(String[] args) {
Logger loggerChain = getChainOfLoggers();
loggerChain.logMessage(Logger.INFO, "mianshiya.com");
loggerChain.logMessage(Logger.DEBUG, "小程序:面试鸭");
loggerChain.logMessage(Logger.ERROR, "网页端:mianshiya.com");
}
}
观察者模式
观察者模式其实也称为发布订阅模式,它定义了对象之间的一种一对多的依赖关系,让多个观察者对象 同时监听某一个主题对象。当主题对象状态发生变化时,它会通知所有观察者对象。
发布订阅与观察者模式的区别**:发布订阅引入了第三方组件来维护发布者和订阅者的关系,他们之间不直接通信,做到了真正解耦。**
观察者模式的组成部分
- Subject(主题/被观察者): 状态发生变化时,通知所有注册的观察者
- Observer(观察者): 接收来自主题的更新通知,并进行相应的操作。
- ConcreteSubject(具体主题): 实现具体的主题对象,保存需要被观察的状态。
- ConcreteObserver(具体观察者): 实现具体的观察者对象,更新自己以与主题的状态同步
我们以一个新闻发布系统来理解下观察者模式。
1)定义接口
新闻发布者(Subject)可以发布新闻,观察者(Observer)是订阅者,希望获取新闻更新
2)实现具体类
- 具体新闻发布者(Concrete Subject)维护一个订阅者列表,并在发布新闻时通知他们
- 具体观察者(Concrete Observer)如报纸、新闻网站等,实现更新方法来展示新闻
3)订阅和退订
订阅者可以订阅(注册)或退订(注销)新闻发布者的新闻。
4)发布新闻
当新闻发布者有新新闻时,"观察者更新自己的新闻内容。它通知所有订阅的观察者
5)客户端代码
客户端代码创建新闻发布者和观察者对象,订阅者选择订新闻,并在接收到新闻时更新显示。
// 定义观察者接口
interface Observer {
void update(String news);
}
// 定义主题接口
interface Subject {
void attach(Observer o);
void detach(Observer o);
void notifyObservers();
}
// 具体新闻发布者
class NewsPublisher implements Subject {
private List<Observer> observers = new ArrayList<>();
public void attach(Observer o) {
observers.add(o);
}
public void detach(Observer o) {
observers.remove(o);
}
public void notifyObservers() {
for (Observer observer : observers) {
observer.update("新闻更新");
}
}
public void publishNews() {
// 假设这里是新闻发布逻辑,会通知发布者里面集合的观察者
notifyObservers();
}
}
// 具体观察者
class NewsPaper implements Observer {
public void update(String news) {
System.out.println("新报 " + news);
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
NewsPublisher publisher = new NewsPublisher(); //发布者
Observer newsPaper = new NewsPaper(); //观察者
publisher.attach(newsPaper); //导入观察者
publisher.publishNews(); //发布新闻
publisher.detach(newsPaper); //移除观察者
publisher.publishNews(); // 此时报纸订阅者不会接收到新闻
}
}
例如消息队列、 Spring 内的监听器机制等都是其应用场景
迭代器模式
迭代器模式,它提供一种方法顺序访问一个集合对象中的各个元素,而又不暴露该对象(可能是数组、链表、树等等)的内部实现。
将遍历逻辑与集合对象的实现分离,提供一致的遍历方式,使得代码统一化,在不改变遍历代码的情况下就能替换底层集合实现。
像 Java 的java.util.Iterator接口和Iterable接口是迭代器模式的直接应用。所有集合类(如ArrayList、HashSet、LinkedList 等)都实现了 Iterable 接口,并提供了 iterator() 方法来获取迭代器,例如,遍历 ArrayList 中的元素:
List<String> list = new ArrayList<>();
list.add("one");
list.add("two");
list.add("three");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
再比如 JDBC 的ResultSet接口遍历数据库査询结果,也是迭代器模式的实现。
迭代器模式的组成部分
- lterator(迭代器接口): 定义访问和历元素的接口
- Aggregate(聚合接口): 定义创建迭代器的接口
- Concretelterator(具体迭代器): 实现迭代器接口,负责遍历聚合对象中的元素
- ConcreteAggregate(具体聚合类): 实现聚合接口,返回一个具体的迭代器实例。
我们来简单实现一个迭代器
//定义迭代器接口
interface Iterator<T> {
boolean hasNext();
T next();
}
//定义聚合接口
interface Aggregate<T> {
Iterator<T> createIterator();
}
//定义具体迭代器
class ConcreteIterator<T> implements Iterator<T> {
private List<T> items;
private int position = 0; //当前遍历位置
public ConcreteIterator(List<T> items) {
this.items = items;
}
@Override
public boolean hasNext() { //如果进行集合遍历时位置<集合大小,则返回true
return position < items.size();
}
@Override
public T next() { //获取当前遍历位置的下一个元素
return items.get(position++);
}
}
//定义具体聚合类
class ConcreteAggregate<T> implements Aggregate<T> {
private List<T> items = new ArrayList<>();
public void addItem(T item) { //添加元素方法
items.add(item);
}
@Override
public Iterator<T> createIterator() {
return new ConcreteIterator<>(items);
}
}
//简单使用
public class Client {
public static void main(String[] args) {
ConcreteAggregate<String> aggregate = new ConcreteAggregate<>();
aggregate.addItem("Item 1"); //添加集合元素
aggregate.addItem("Item 2");
aggregate.addItem("Item 3");
Iterator<String> iterator = aggregate.createIterator(); //返回对应该集合的迭代器
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
命令模式
命令模式(Command Pattern)是一种行为设计模式,它将请求封装成对象,使得请求参数化,便于对请求排队或记录请求日志,以及支持可撤销的操作。
简单看下以开关灯为例实现命令模式的代码,更容易理解上面那段含义:
// 命令接口
interface Command {
void execute();
}
// 接收者
class Light {
public void on() { //用于控制灯光的开关
System.out.println("The light is on.");
}
public void off() {
System.out.println("The light is off.");
}
}
// 具体命令 开灯
class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
}
// 具体命令 关灯
class LightOffCommand implements Command {
private Light light;
public LightOffCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.off();
}
}
// 调用者,传入了开关命令,因此可以开关灯
class RemoteControl {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Light light = new Light();
Command lightOn = new LightOnCommand(light); //开灯
Command lightOff = new LightOffCommand(light); //关灯
RemoteControl remote = new RemoteControl();
remote.setCommand(lightOn);
remote.pressButton(); //开灯
remote.setCommand(lightOff);
remote.pressButton(); //关灯
}
}
状态模式
状态模式 允许对象在其内部状态发生改变时改变其行为,将状态的行为封装在独立的类中,并将这些状态对象组合在拥有状态的对象中,这样就可以在状态改变时切换状态对象,从而改变对象的行为
它主要是状态机的一种实现方式,状态机可分为: 状态、事件、动作三个部分。事件的触发就会导致状态的改变,并且可作出一定的动作(也可以没有动作,只有状态的改变)
// 定义状态接口
interface State {
void handle(Context context);
}
// 上下文环境,状态模式的核心,它持有一个 State 对象,并提供了 setState 方法来改变当前状态
class Context {
private State state;
public Context(State state) {
this.state = state;
}
public void setState(State state) {
this.state = state;
}
public void request() {
state.handle(this);
}
}
// 具体实现类
//AB每个类在处理请求时都会打印一条消息,并且会改变上下文 Context 的状态为另一种状态
class ConcreteStateA implements State {
@Override
public void handle(Context context) {
System.out.println("State A is handling the request.");
context.setState(new ConcreteStateB());
}
}
class ConcreteStateB implements State {
@Override
public void handle(Context context) {
System.out.println("State B is handling the request.");
context.setState(new ConcreteStateA());
}
}
public class Client {
public static void main(String[] args) {
Context context = new Context(new ConcreteStateA()); //新建A状态环境
context.request(); // State A is handling the request. //请求A状态,然后设置当前状态为B
context.request(); // State B is handling the request.
context.request(); // State A is handling the request.
context.request(); // State B is handling the request.
}
}
中介者模式
中介模式通过引入了一个中介对象,来封装一组对象之间的交互,来避免对象之间的直接交互。通过引入一个中介者对象,使对象之间的关系变得简单且易于维护。
听起来不就是和现实生活中的中介一样嘛。
引入中介模式的原因:多对象交互可能会使得关系图很混乱,代码也不清晰,让多对象都和中介交互就能避免这点
聊天室实现其实就是运用了中介模式。信息的传递都由服务器这个中介来做,所有用户都把消息发给服务器,不然如果点对点传输大家可以想象下有多复杂。我们可以简单的看下聊天室的实现:
interface ChatMediator { //中介者接口
void sendMessage(String message, User user);
void addUser(User user);
}
class ChatMediatorImpl implements ChatMediator { //具体中介者
private List<User> users; //存储加入的用户
public ChatMediatorImpl() {
this.users = new ArrayList<>();
}
@Override
public void addUser(User user) {
this.users.add(user);
}
@Override
public void sendMessage(String message, User user) {
for (User u : this.users) {
if (u != user) {
u.receive(message);
}
}
}
}
abstract class User { //抽象用户
protected ChatMediator mediator;
protected String name;
public User(ChatMediator mediator, String name) {
this.mediator = mediator;
this.name = name;
}
public abstract void send(String message);
public abstract void receive(String message);
}
class UserImpl extends User { //用户
public UserImpl(ChatMediator mediator, String name) {
super(mediator, name);
}
@Override
public void send(String message) {
System.out.println(this.name + " sends: " + message);
mediator.sendMessage(message, this);
}
@Override
public void receive(String message) {
System.out.println(this.name + " receives: " + message);
}
}
public class ChatClient { //客户端
public static void main(String[] args) {
ChatMediator mediator = new ChatMediatorImpl();
User user1 = new UserImpl(mediator, "Alice");
User user2 = new UserImpl(mediator, "Bob");
User user3 = new UserImpl(mediator, "Charlie");
mediator.addUser(user1); //用户加入中介
mediator.addUser(user2);
mediator.addUser(user3);
user1.send("Hello, everyone!");
}
}
访问者模式
访问者模式它将数据结构与操作分离,使得你可以在不改变数据结构的前提下定义新的操作。访问者模式通过将操作封装到独立的访问者对象中,使得新的操作可以很容易地添加到系统中。
例如对象序列化场景,比如需要将对象转成 JSON 或者 XML 等格式,利用访问者模式将对象的结构和序列化操作分离,这样就能很方便的扩展新的序列化格式。先看下示例代码:
//访问者接口(类比序列化接口)
interface Visitor {
void visit(ElementA element);
void visit(ElementB element);
}
//具体访问者类(类比具体序列化实现)
class ConcreteVisitor implements Visitor {
@Override
public void visit(ElementA element) {
System.out.println("Processing ElementA: " + element.getName());
}
@Override
public void visit(ElementB element) {
System.out.println("Processing ElementB: " + element.getName());
}
}
//需要对序列化的元素接口
interface Element {
void accept(Visitor visitor);
}
// 具体被序列化的元素A
class ElementA implements Element {
private String name;
public ElementA(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// 具体被序列化的元素B
class ElementB implements Element {
private String name;
public ElementB(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
//对象结构,包含很多元素
class ObjectStructure {
private List<Element> elements = new ArrayList<>();
public void addElement(Element element) {
elements.add(element);
}
public void accept(Visitor visitor) {
for (Element element : elements) {
element.accept(visitor);
}
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
// 组装对象
ObjectStructure objectStructure = new ObjectStructure();
objectStructure.addElement(new ElementA("Element A1"));
objectStructure.addElement(new ElementB("Element B1"));
objectStructure.addElement(new ElementA("Element A2"));
//访问者 (如果是序列化场景,ConcreteVisitor 可以当做 JSON 序列化)
Visitor visitor = new ConcreteVisitor();
objectStructure.accept(visitor);
// 假设后面要替换 xml 序列化,仅需新建 xml 的访问者,然后传入到对象内部即可
Visitor visitorXML = new XMLVisitor();
objectStructure.accept(visitorXML);
}
}
可以看到,将数据结构和操作分离开之后,如果要替换具体的操作,仅需新增一个操作即可,不需要修改任何数据结构,这就符合开闭原则,也保证了类的职责单一。
备忘录模式
备忘录模式指的是在不违背封装原则的前提下,捕获对象内部的状态,将其保存在外部,便于后面对象恢复之前的状态,使得系统更具灵活性和可维护性。
听来像不像保留个快照作为备份,后面基于这个快照进行恢复? 所以备忘录模式其实也叫快照模式。主要用于撤销、恢复等场景。
来看下示例代码,我先大致解释一下几个类的含义:
- Memento,存储状态的备忘录
- Originator,需要备份状态的对象类
- Caretaker,管理者,仅保存备忘录
// 备忘录类
class Memento {
private String state;
public Memento(String state) {
this.state = state;
}
public String getState() {
return state;
}
}
// 需要备份状态的对象类
class Originator {
private String state;
public void setState(String state) {
this.state = state;
System.out.println("State set to: " + state);
}
public String getState() {
return state;
}
public Memento createMemento() {
return new Memento(state);
}
public void restoreMemento(Memento memento) {
this.state = memento.getState();
System.out.println("State restored to: " + state);
}
}
//管理存储类
class Caretaker {
private Memento memento;
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
}
//客户端类
public class Client {
public static void main(String[] args) {
Originator originator = new Originator(); //对象类
Caretaker caretaker = new Caretaker(); //存储类
originator.setState("State1");
caretaker.setMemento(originator.createMemento()); //保存快照
originator.setState("State2");
System.out.println("Current State: " + originator.getState());
originator.restoreMemento(caretaker.getMemento()); //恢复快照
System.out.println("Restored State: " + originator.getState());
}
}
如果直接利用 set 方法修改内部状态,就违反了封装的原则,因为如果你暴露了 set 方法,则对象内部的状态很可能被别的业务调用修改了。而 restoreMemento 是一个很清晰的方法定义,即恢复之前的状态,不会被乱用。这也是备忘录模式的前提,不违反封装原则。

被折叠的 条评论
为什么被折叠?



