这篇文章我们来讲解下行为型设计模式:主要用于描述类或对象之间的交互或职责的分配,为设计类的交互和职责分配做指南。
责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。
在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
优点:
举个例子:在平时开发中,我们经常需要打印日志来查看一些信息,但打日志时我们都会指定一个日志级别,比如 INFO
、WARN
、ERROR
等,指定不同的级别打印的日志存储的文件是不同的,这种场景就可以使用责任链设计模式来设计解决。
下面使用程序演示下上面的例子:
public interface LoggerInterFace {
//指定下一个目标
void setNextLogger(LoggerInterFace nextLogger);
//打印日志
void logMessage(int level, String message);
//日志格式
void writeFormat(String message);
}
public abstract class AbstractLogger implements LoggerInterFace {
public static int INFO = 1;
public static int WARN = 2;
public static int ERROR = 3;
private int level;
private LoggerInterFace nextLogger;
public AbstractLogger(int level) {
this.level = level;
}
@Override
public void setNextLogger(LoggerInterFace nextLogger) {
this.nextLogger = nextLogger;
}
@Override
public void logMessage(int level, String message) {
if (this.level == level) {
writeFormat(message);
}
if (nextLogger != null) {
nextLogger.logMessage(level, message);
}
}
}
INFO
级别的实现public class InfoLogger extends AbstractLogger {
public InfoLogger(int level) {
super(level);
}
@Override
public void writeFormat(String message) {
System.out.println(StringFormatter.concat(" 普通信息:", message).getValue());
}
}
WARN
级别的实现public class WarnLogger extends AbstractLogger {
public WarnLogger(int level) {
super(level);
}
@Override
public void writeFormat(String message) {
System.out.println(StringFormatter.concat(" 警告信息:", message).getValue());
}
}
ERROR
级别的实现public class ErrorLogger extends AbstractLogger {
public ErrorLogger(int level) {
super(level);
}
@Override
public void writeFormat(String message) {
System.out.println(StringFormatter.concat(" 错误信息:", message).getValue());
}
}
public class LoggerFactory {
public static LoggerInterFace getLogger() {
//定义责任链的顺序
List<AbstractLogger> list = new LinkedList<>();
list.add(new ErrorLogger(AbstractLogger.ERROR));
list.add(new WarnLogger(AbstractLogger.WARN));
list.add(new InfoLogger(AbstractLogger.INFO));
for (int i = 0; i < list.size() - 1; i++) {
list.get(i).setNextLogger(list.get(i + 1));
}
return list.get(0);
}
}
public class demo {
public static void main(String[] args) {
LoggerInterFace logger = LoggerFactory.getLogger();
logger.logMessage(AbstractLogger.INFO, " 打印普通日志");
logger.logMessage(AbstractLogger.WARN, " 打印警告日志");
logger.logMessage(AbstractLogger.ERROR, " 打印错误日志");
}
}
命令模式(Command Pattern)是一种数据驱动的设计模式,请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。
优点:降低了系统耦合度,新的命令可以很容易添加到系统中去。
用画各种形状的例子来说,可以采用命令设计模式来设计,绘画不同的形状就将对应形状的实现传递给命令执行器,以一种统一的命令的形式来做不同的操作。可以有效降低耦合性和提高扩展性。
下面使用程序演示下:
public interface ShapeInterFace {
void draw();
}
public class CircleShape implements ShapeInterFace {
@Override
public void draw() {
System.out.println("画圆形");
}
}
public class RectangleShape implements ShapeInterFace {
@Override
public void draw() {
System.out.println("画矩形!");
}
}
public interface CommandInterFace {
void execute();
}
public abstract class CommandAbstract implements CommandInterFace {
protected ShapeInterFace shape;
public CommandAbstract(ShapeInterFace shape) {
this.shape = shape;
}
}
public class ShapeCommand extends CommandAbstract {
public ShapeCommand(ShapeInterFace shape) {
super(shape);
}
@Override
public void execute() {
shape.draw();
}
}
public class Invoker {
private CommandInterFace command;
public void setCommand(CommandInterFace command) {
this.command = command;
}
public void executeCommand() {
command.execute();
}
}
public class demo {
public static void main(String[] args) {
Invoker invoker = new Invoker();
invoker.setCommand(new ShapeCommand(new CircleShape()));
invoker.executeCommand();
invoker.setCommand(new ShapeCommand(new RectangleShape()));
invoker.executeCommand();
}
}
迭代器模式(Iterator Pattern)是 Java 和 .Net 编程环境中非常常用的设计模式。这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。
优点:
下面我们使用程序看下迭代器的设计过程:
public interface Iterator {
public boolean hasNext();
public Object next();
}
public class NameIterator implements Iterator {
private int index;
private String[] names;
public NameIterator(String[] names) {
this.names = names;
}
@Override
public boolean hasNext() {
if (index < names.length) {
return true;
}
return false;
}
@Override
public Object next() {
if (this.hasNext()) {
return names[index++];
}
return null;
}
}
public class demo {
public static void main(String[] args) {
String[] names = {"A" , "B" ,"C" , "D"};
NameIterator iterator = new NameIterator(names);
while (iterator.hasNext()){
String name = (String)iterator.next();
System.out.println(name);
}
}
}
模板模式(Template Pattern),又叫模板方法设计模式,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。
优点:有封装不变扩展可变, 公共代码便于维护,行为由父类控制子类负责具体实现。
举个例子:拿画形状的案例来说,比如我们画一个图像之后还要加边框,还要画颜色,如果我们画每个图像都要再考虑怎么加入边框和颜色,就显得有点臃肿,因为加入边框和颜色对所有的图像都是一致的,此时就可以将图形边框和颜色在一个统一的模板中定义规则,具体的实现放在具体的子类中。
下面使用程序演示下上面的例子:
public interface ShapeInterFace {
void drawShape();
void drawBorder();
void drawColor();
void genShape();
}
public abstract class ShapeTemplate implements ShapeInterFace {
@Override
public void genShape() {
System.out.println("开始整合图形");
drawShape();
drawBorder();
drawColor();
System.out.println("整合完毕");
}
}
public class CircleShapeImpl extends ShapeTemplate {
@Override
public void drawShape() {
System.out.println("画圆形");
}
@Override
public void drawBorder() {
System.out.println("画圆形的边框");
}
@Override
public void drawColor() {
System.out.println("画圆形的颜色");
}
}
public class demo {
public static void main(String[] args) {
ShapeInterFace shape = new CircleShapeImpl();
shape.genShape();
}
}
策略模式(Strategy Pattern),一个类的行为或其算法可以在运行时更改。我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context
对象。策略对象改变 context
对象的执行算法。
采用画图形的例子来说,画不同的图形,通过一个标识来做动作,而不是调用具体的类。
下面使用程序演示下上面的例子:
public interface ShapeInterFace {
void draw();
}
public class CricleShape implements ShapeInterFace {
@Override
public void draw() {
System.out.println("画圆形!");
}
}
public class RectangleShape implements ShapeInterFace {
@Override
public void draw() {
System.out.println("画矩形");
}
}
public class TriangleShape implements ShapeInterFace {
@Override
public void draw() {
System.out.println("画三角形");
}
}
public class FactoryStrategy {
private static Map<String, ShapeInterFace> strategys = new ConcurrentHashMap<String, ShapeInterFace>();
static {
strategys.put("circle", new CricleShape());
strategys.put("rectangle", new RectangleShape());
strategys.put("triangle", new TriangleShape());
}
public static ShapeInterFace getStrategy(String strategyId) {
return strategys.get(strategyId);
}
}
public class demo {
public static void main(String[] args) {
FactoryStrategy.getStrategy("circle").draw();
FactoryStrategy.getStrategy("rectangle").draw();
FactoryStrategy.getStrategy("triangle").draw();
}
}
解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL
解析、符号处理引擎等。
举个例子,拿SQL
解析来看,我们写的一般都是SQL
指令然后交给数据库就可以拿到我们想要的数据了,数据库是怎么知道我们查那张表的那些数据呢,其实我们将SQL
指令交给数据库后,数据库就在对SQL
进行解释,检查语法是否正确,并确定查询的表,查询的字段,和条件等。
下面使用程序演示下上面的例子,这里简单的做下SQL
解析,便于理解这种设计模式:
SQL
解析接口public interface SqlnterFace {
SqlnterFace interpretation(String sql);
void execute();
}
SELECT
查询的实现public class SelectSqlImpl implements SqlnterFace {
String sql;
String columns;
String table;
String condition;
@Override
public SqlnterFace interpretation(String sql) {
System.out.println("开始解释SQL ");
if (!sql.contains("SELECT")) {
System.out.println("SQL 指令错误!");
return this;
}
this.sql = sql;
this.columns = sql.split("FROM")[0].split(" ")[1].trim();
this.table = sql.split("FROM")[1].split("WHERE")[0].trim();
this.condition = sql.split("WHERE")[1].trim();
System.out.println("解释结果: 查询的表为:" + this.table + " 查询的列为:" + this.columns + " 条件:" + this.condition);
return this;
}
@Override
public void execute() {
System.out.println("执行SQL: " + this.sql);
}
}
public class demo {
public static void main(String[] args) {
String sql = "SELECT name,age FROM user WHERE id=1 ";
SqlnterFace sqlExe = new SelectSqlImpl();
sqlExe.interpretation(sql).execute();
}
}
观察者模式,当一个对象被修改时,则会自动通知依赖它的对象。对象间存在一对多关系时就可以选用这个设计模式。
优点:观察者和被观察者是抽象耦合的。
举个例子:做通知类型的业务时,可能需要同时发送短信和邮件,如果说在需要发送的时候,我们就每个都调用一次发送的方法,就显得代码有些冗余了,采用观察者设计模式则可以有效解决此类问题。
下面使用程序演示下上面的例子:
public interface MsgListener<T> {
void msgEvent(T msg);
}
bean
对象@Data
@AllArgsConstructor
public class MyMsgEvent {
private int code;
private String msg;
}
@Slf4j
@Component
public class MyMailLinstener implements MsgListener<MyMsgEvent> {
@Override
public void msgEvent(MyMsgEvent msg) {
System.out.println("邮件监听触发" + msg.toString());
}
}
@Slf4j
@Component
public class MySMSListenter implements MsgListener<MyMsgEvent> {
@Override
public void msgEvent(MyMsgEvent msg) {
System.out.println("短信监听触发" + msg.toString());
}
}
@Slf4j
@Component
public class MsgPublic {
private static List<MsgListener> list = new ArrayList<>();
static {
list.add(new MyMailLinstener());
list.add(new MySMSListenter());
}
public static void publicMsg(Object o) {
list.forEach(listener -> listener.msgEvent(o));
}
}
public class demo {
public static void main(String[] args) {
MyMsgEvent event = new MyMsgEvent(200, "success");
MsgPublic.publicMsg(event);
}
}
扩展:
上面的在消息发布类中时写死的目前有哪些实现,如果不写死可以使用Spring
来获取MsgListener
下的所有子类来发送信息:
@Slf4j
@Component
public class MsgPublic implements ApplicationRunner, ApplicationContextAware {
private static ApplicationContext applicationContext;
private List<MsgListener> list = new ArrayList<>();
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if(this.applicationContext == null) {
this.applicationContext = applicationContext;
}
}
@Override
public void run(ApplicationArguments args) throws Exception {
Map<String, MsgListener> beanOfType = applicationContext.getBeansOfType(MsgListener.class);
beanOfType.forEach((key,val) -> {
list.add(val);
});
log.info("注册监听数:"+beanOfType.size());
}
public void publicMsg(Object o) {
list.forEach(listener -> {
listener.msgEvent(o);
});
}
}
另外,在Spring
中,就有这种封装ApplicationListener
来便于我们使用,下面看下Spring
的使用:
bean
public class MsgInfo extends ApplicationEvent {
private int code;
private String msg;
public MsgInfo(Object source) {
super(source);
}
public MsgInfo(Object source, int code, String msg) {
super(source);
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
@Override
public String toString() {
return "MsgInfo{" +
"code=" + code +
", msg='" + msg + '\'' +
'}';
}
}
@Slf4j
@Component
public class SMSListener implements ApplicationListener<MsgInfo> {
@Override
public void onApplicationEvent(MsgInfo msgInfo) {
log.info("短信监听触发" + msgInfo.toString());
}
}
@Slf4j
@Component
public class EmailListener implements ApplicationListener<MsgInfo> {
@Override
public void onApplicationEvent(MsgInfo msgInfo) {
log.info("邮件监听触发" + msgInfo.toString());
}
}
@RestController
public class SendTest {
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
@GetMapping("/TestPublish")
public String testPublish() {
applicationEventPublisher.publishEvent(new MsgInfo(this, 200, "abc"));
return "success";
}
}
状态模式(State Pattern),类的行为是基于它的状态改变的。我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context
对象。
举个例子,在用户登录状态方面,有登录状态,登出状态等,在登录和登出时,我们不仅要改变状态还要做一些附加的操作,那如果我们修改状态时,就能自动触发附加操作,那岂不是代码的维护性更高了,采用状态模式就可以解决这类问题。
下面使用程序演示下上面的例子:
public interface StateInterFace {
void login(Context context);
}
public class LoginStatus implements StateInterFace {
@Override
public void login(Context context) {
System.out.println("登录的附加操作!");
System.out.println("已经切换到登陆状态");
}
}
public class LoginOutStatus implements StateInterFace {
@Override
public void login(Context context) {
System.out.println("登出附加操作!");
System.out.println("已切换到登出状态!");
}
}
Context
public class Context {
public static final StateInterFace LOGIN_STATUS = new LoginStatus();
public static final StateInterFace LOGINOUT_STATUS = new LoginOutStatus();
public static final StateInterFace DEFAULT_STATUS = LOGIN_STATUS;
public void setState(StateInterFace state) {
state.login(this);
}
}
public class demo {
public static void main(String[] args) {
Context context = new Context();
context.setState(Context.LOGIN_STATUS);
context.setState(Context.LOGINOUT_STATUS);
}
}
中介者模式(Mediator Pattern)是用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护。
优点:为了降低类的复杂度,将一对多转化成了一对一,各个类之间的解耦,符合迪米特原则。
举个例子:比如我们发送消息,我们肯定要调用发送消息的封装,肯定需要传递消息内容和消息的接收人信息这两个参数吧,但问题来了,我要发给小明要把消息接收人指定为小明的人员信息,要发给小红要把消息接收人指定为小红的人员信息。现在我们就像既然我都拿到接受人的信息了,为什么不能直接给他发送呢,反而多了一步调用发消息的封装。采用中介者模式的思想,我们可以在每个人员信息的类中去调用发消息的封装,就省去了我们多的哪一步,此时中介者就是人员信息类。
下面使用程序演示下上面的例子:
public class ChatRoom {
public static void showMessage(User user, String message) {
System.out.println("发送信息:" + message + ", 接收人:" + user);
}
}
@Data
@AllArgsConstructor
public class User {
private String name;
public void sendMessage(String message){
ChatRoom.showMessage(this,message);
}
}
public class demo {
public static void main(String[] args) {
User robert = new User("小明");
User john = new User("小红");
robert.sendMessage("哈哈哈");
john.sendMessage("嗯嗯");
}
}