一、初识设计模式
设计模式的目的是为了重用代码,让代码更容易被他人理解,以及保证代码可靠性。
总体来说,设计模式分为三大类:
创建型模式:共5种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式
结构型模式:共7种:适配器模式、装饰器模式、代理模式、桥接模式、外观模式、组合模式、享元模式
行为型模式:共11种:策略模式、模板方法模式、观察者模式、责任链模式、访问者模式、中介者模式、迭代器模式、命令模式、状态模式、备忘录模式、解释器模式
二、模板模式
模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。即应该让父类成为子类的模板,所有重复的代码都应该上升到父类,而不是让每个子类都去重复,子类只负责各自不同的算法步骤。
1)适用场景
工作中遇到一个场景,就是几乎系统的每一个页面都会去调用同一个方法,但是调用方法前的逻辑不一致。
如: function A(){
B; //各个页面自己的逻辑
C;//通用逻辑
}
这时可以想到模板模式,将公共的方法C放父类去,子类做不同的功能实现。
2)代码
用法: 将类中经常改变或者可能改变的部分提取为作为一个抽象接口类,然后在类中包含这个对象的实例,这样类实例在运行时就可以随意调用实现了这个接口的类的行为。
父类和子类,父类中的模板方法给出了大致的逻辑,但是每个方法需要子类自己实现
/**
* 模板类
*/
public abstract class StrategyHandler {
//抽象方法 供不同子类实现
public abstract void method();
public void equalWay(){
method();
//模板方法
System.out.println("ABC通用方法");
}
}
/**
* 第一个实现类
*/
@Service("strategyImplA")
public class StrategyImplA extends StrategyHandler {
public void method() {
System.out.println("第一个实现类的方法");
}
}
/**
* 第二个实现类
*/
@Service("strategyImplB")
public class StrategyImplB extends StrategyHandler {
public void method() {
System.out.println("第二个实现类的方法");
}
}
/**
* 第三个实现类
*/
@Service("strategyImplC")
public class StrategyImplC extends StrategyHandler {
public void method() {
System.out.println("第三个实现类的方法");
}
}
/**
* 模板枚举类
*/
public enum TypeEnum {
A("strategyImplA", "第一个实现类"),
B("strategyImplB", "第二个实现类"),
C("strategyImplC", "第三个实现类"),
;
private String handler;
private String description;
TypeEnum(String handler, String description) {
this.handler = handler;
this.description = description;
}
public String getHandler() {
return handler;
}
public void setHandler(String handler) {
this.handler = handler;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
@SpringBootApplication
public class DemoApplication {
//启动类里 初始化ApplicationContext 1
private static ApplicationContext context;
// 初始化ApplicationContext 2
@Bean
public ApplicationContext getApplicationContext() {
return context;
}
public static void main(String[] args) {
//初始化ApplicationContext 3 1,2,3固定位置放
context = SpringApplication.run(DemoApplication.class, args);
// 若别的Controller需要获得实现类bean 则这样引入bean: @Autowired ApplicationContext context;
//用枚举值获得实现类bean
StrategyHandler serviceA = (StrategyHandler)context.getBean(TypeEnum.A.getHandler());
serviceA.equalWay();
StrategyHandler serviceB = (StrategyHandler)context.getBean(TypeEnum.B.getHandler());
serviceB.equalWay();
StrategyHandler serviceC = (StrategyHandler)context.getBean(TypeEnum.C.getHandler());
serviceC.equalWay();
}
}
打印结果:
第一个实现类的方法
ABC通用方法
第二个实现类的方法
ABC通用方法
第三个实现类的方法
ABC通用方法
除了用枚举获得实现类,还可以用环境类:
public class Context {
StrategyHandler strategyHandler;
public Context(StrategyHandler strategyHandler) {
this.strategyHandler = strategyHandler;
}
public void testHandler(){
strategyHandler.equalWay();
}
}
调用的时候只需:
//不用枚举类 直接给出实现类
Context ctx = new Context(new StrategyImplA());
ctx.testHandler();
打印结果:
第一个实现类的方法
ABC通用方法
3)优缺点
优点:
1)模板模式符合开闭原则(对拓展开放,对修改关闭);
2)避免使用多重条件转移语句,如if...else...语句和switch 语句;
3)恰当使用继承可以把公共的代码移到父类里面(当某个类继承父类,即可调用父类的非私有方法),
从而避免代码重复。
缺点:很明显就是实现类很多
三、策略模式
策略模式定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。
1)适用场景
(1)应用程序需要实现特定的功能服务,而该程序有多种实现方式使用,所以需
要动态地在几种算法中选择一种;
(2)一个类定义了多种行为算法,并且这些行为在类的操作中以多个条件语句的
形式出现,就可以将相关的条件分支移入它们各自的Strategy类中以代替这些条
件语句。
(3)策略模式就是用来封装算法的,在实践中,可以用策略模式封装几乎任何类型的规则,
只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式。
选择哪种策略是客户端决定的,可以把new哪种策略改为switch结构,甚至使用反射。
2)代码
//策略接口类
public interface Strategy {
void algorithm();
}
//不同的策略的实现类
public class ConcreteStrategyA implements Strategy{
@Override
public void algorithm() {
System.out.println("算法A");
}
}
public class ConcreteStrategyB implements Strategy{
@Override
public void algorithm() {
System.out.println("算法B");
}
}
public class ConcreteStrategyC implements Strategy{
@Override
public void algorithm() {
System.out.println("算法C");
}
}
//环境类
public class Context {
private Strategy strategy;
public Context(String type) throws Exception {
switch (type){
case "A":
strategy = new ConcreteStrategyA();
break;
case "B":
strategy = new ConcreteStrategyB();
break;
case "C":
strategy = new ConcreteStrategyC();
break;
default:
throw new Exception();
}
}
public void contextInterface(){
if(!Objects.isNull(strategy)){
strategy.algorithm();
}
}
}
//方法调用
public class TestdemoApplication {
public static void main(String[] args) {
Context context = new Context("A");
context.contextInterface();
}
}
模板类是抽象类,含模板方法,策略类是接口类,只有各自不同的策略子类算法,无论是模板类还是策略类,子类的算法都是特定独立的,但是状态模式的各个子类算法是相互关联的。
四、状态模式
1)适用场景
1、当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,可以使用状态模式。
2、状态模式通过把各种状态转移逻辑分布到State的子类中
2)代码
//状态类
public interface State {
public void handle(Context context);
}
//状态子类
public class ConcreteStateA implements State{
@Override
public void handle(Context context) {
//状态切为B
context.setState((State) new ConcreteStateB());
}
}
public class ConcreteStateB implements State{
@Override
public void handle(Context context) {
//状态切为C
context.setState((State) new ConcreteStateC());
}
}
public class ConcreteStateC implements State{
@Override
public void handle(Context context) {
context.setState((State) new ConcreteStateA());
}
}
//环境类
public class Context {
private State state;
public Context(State state) {
this.state = state;
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
System.out.println("当前状态:" + this.state.getClass().getName());
}
public void request(){
this.state.handle(this);
}
}
//主方法调用
public class TestdemoApplication {
public static void main(String[] args) {
Context context = new Context(new ConcreteStateA());
context.request();
context.request();
context.request();
context.request();
}
}
打印结果
当前状态:com.example.design.state.ConcreteStateB
当前状态:com.example.design.state.ConcreteStateC
当前状态:com.example.design.state.ConcreteStateA
当前状态:com.example.design.state.ConcreteStateB