原型模式使用场景
细胞分列式的New出大量重复对象
未优化时代码:
开发代码中常见的循环赋值装载进集合的代码,这段代码在大多数场景下是没问题的,但是在要求高性能的场景下可能存在性能不好的情况
代码逻辑其实很简单就是new对象赋值装载到List中
public class BokeTest {
public static void main(String[] args){
List<User> list = new ArrayList<>();for(;;){
User user = new User();user.setId( new Random().nextLong() );user.setName( "xxx" );list.add( user );}
}
}
优化后的代码:
原型模式 & 对象工厂优化:
通过浅拷贝的方式来变大量new实例化的过程,new关键字的运行过程可以参考对象生成的过程 (加载-验证-准备-解析-初始化),浅拷贝的过程就简单很多了(值传递)
@Data
@ToString
public class User implements Cloneable{
private Long id;private String name;//实现浅拷贝
@Override
protected User clone() throws CloneNotSupportedException {
return (User) super.clone();}
}
/** * <p>Title:用户对象生成工厂</p> * <p>Description:</p> * * @author QIQI * @date*/public class UserFactory {
//只需要new一次对象
private static User userProtoType = new User();public static User getInstance() throws CloneNotSupportedException {
User user = userProtoType.clone();return user;}
}
public class BokeTest {
public static void main(String[] args) throws CloneNotSupportedException {
List<User> list = new ArrayList<>();for(;;){
User userX = UserFactory.getInstance();userX.setId( new Random().nextLong() );userX.setName( "xxx" );list.add( userX );}
}
}
结论:
某个接口QPS高 & RT要求很低,这个时候可以对代码的new实例化进行优化加速性能,但是需要注意的是对象必须属性不包含其他引用对象,不然浅拷贝是有隐藏BUG的需要使用深拷贝,深拷贝常用的方式是序列化和反序列化进行加速
装饰模式使用场景:
多态化的综合使用,针对不同功能进行多态化的抽象,将原始行为层层组装包裹对外展现统一的调用入口
业务需求:
化妆功能,完成化妆后可以展现化妆后的样貌
第一期实现涂口红的化妆功能
第二期实现涂眼影功能
未优化时代码:
下面代码是我们大多数开发的实现方式,将涂口红、涂眼影的逻辑抽成2个私有方法,入口代码组装在统一输出化妆后样貌,如果需求未来不发生变化和不需要迭代的话这样的代码也没什么问题,只是大多数情况功能需求都是会发生迭代的
1.假设涂口红的逻辑越来越复杂,需要针对不同品牌、不同色号、不同干湿度来进行更加多样的的化妆后样貌展现,这个时候涂口红的私有方法会越来越庞大,就算再拆几个私有方法出来也无法解决整个分支判断代码的无法可读性
2.假设show的展现方式也越来越丰富,那么最终show的代码也需要很多判断逻辑来选择调用那种show
//口红model
@Data
@ToString
public class Rouge{
//颜色
private String colour;//湿润度
private String wetness;}
//眼影model
@Data
@ToString
public class EyeShadow{
//颜色
private String colour;}
/*
* <p>Title:根据传入的口红品牌给女孩上口红</p>
* <p>Description:</p>
* @author QIQI
* @params [rougeBrand]
* @return void
* @throws
* @date 2023/03/13 18:25
*/
private static void girlRouge(RougeBrand rougeBrand){
//女孩上口红逻辑
System.out.println("xxx使用了口红功能,色号:" + rougeBrand.getColour() + ",湿润度:" + rougeBrand.getWetness());}
/*
* <p>Title:根据传入的口红品牌给女孩上口红</p>
* <p>Description:</p>
* @author QIQI
* @params [rougeBrand]
* @return void
* @throws
* @date 2023/03/13 18:25
*/
private static void girl EyeShadow(EyeShadow eyeShadow){
//女孩涂眼影逻辑
System.out.println("xxx使用了眼影功能");}
public static void main(String[] args) throws CloneNotSupportedException {
girlRouge() + EyeShadow();
//完成了化妆动作后进行展现
show();
}
上面的代码因为二期的迭代在不同的私有方法中都加入了新的实现以及最终加了一个是否加分的判断逻辑,如果继续迭代下去功能越来越复杂此时代码会很难维护(修改一个小逻辑可能影响全局判断)
优化后的代码:
/** * <p>Title:化妆功能基类接口</p> * <p>Description: * 抽象基础功能接口 * </p> * * @author QIQI * @date*/public interface DecoatorI {
/*
* <p>Title:化妆完成后展示定义</p>
* <p>Description:</p>
* @author QIQI
* @params []
* @return void
* @throws
* @date 2023/03/14 20:28
*/
void show();}
/** * <p>Title:化妆装饰抽象装饰类</p> * <p>Description:</p> * * @author QIQI * @date*/public abstract class Decoator implements DecoatorI {
DecoatorI decoatorI;public Decoator(DecoatorI decoatorI) {
this.decoatorI = decoatorI;}
@Override
public void show() {
decoatorI.show();}
}
/** * <p>Title:抽象实现,普通场景的show实现</p> * <p>Description:</p> * * @author QIQI * @date*/public class CommonShow implements DecoatorI {
@Override
public void show() {
System.out.println("普通展示");}
}
public interface RougeService{
/*
* <p>Title:口红接口定义</p>
* <p>Description:</p>
* @author QIQI
* @params [rougeBrand]
* @return void
* @throws
* @date 2023/03/14 15:39
*/
void rouge(Rouge rouge);}
/*** <p>Title:口红定义,不同品牌不同子类实现</p>* <p>Description:</p>* @author QIQI* @date * */public interface RougeService{
/*
* <p>Title:口红接口定义</p>
* <p>Description:</p>
* @author QIQI
* @params [rougeBrand]
* @return void
* @throws
* @date 2023/03/14 15:39
*/
void rouge(Rouge rouge);}
/** * <p>Title:X品牌口红实现</p> * <p>Description:</p> * * @author QIQI * @date*/public class BrandXRougeServiceImpl extends Decoator implements RougeService{
private Rouge rouge;public BrandXRougeServiceImpl(DecoatorI decoatorI,Rouge rouge) {
super( decoatorI );this.rouge = rouge;}
@Override
public void show() {
//用了品牌X的口红
rouge(rouge);//展现状态
decoatorI.show();}
@Override
public void rouge(Rouge rouge) {
System.out.println("品牌X的口红实现");}
}
public class BokeTest {
public static void main(String[] args) throws CloneNotSupportedException {
Decoator decoator = new BrandXRougeServiceImpl( new CommonShow(),new Rouge() );decoator.show();}
}
整个结构图如下,拆分描述:
DecoatorI 被装饰类,里面有一个show方法等待子类实现,只定义了最后需要有一个展示行为,但是过程是什么样的被装饰类不清楚
Decoator 装饰类,实现了被装饰类的show方法,但是也只是抽象实现,并没有具体逻辑,真正的show逻辑还是需要子类完成
为什么要抽象实现show方法呢,原因是因为为了节省时间使用了最简单的案例说明,如果是复杂需求可能会存在show1、show2、show3等等多种展示形式,那么如果不通过抽象实现的话就会造成所有子类都要实现每一个show,比如口红行为只需要show1
CommonShow 普通show的实现,可以有多种show的实现,最终传进Decoator装饰类和不同化妆品进行组装嵌套输出
RougeService 口红的行为定义,这里再次扩展了多态的可能,不同品牌的口红效果可能不一样,案例随便实现了一个X品牌的口红

策略模式的使用场景:
1、许多相关的类仅仅是行为有异;
2、一个类定义了多种行为,并且这些行为在类的操作中以多个条件语句的形式出现。
未优化时代码:
根据入参来判断执行哪段私有方法逻辑,这种场景其实在我们实战中很常见,也并不是说所有的类似场景都要改成策略模式,因为策略模式的缺点就是子类太多,个人建议这种分支逻辑在 >= 3 && <=10 的时候优化成策略模式,分支逻辑太少没有优化必要反而增加复杂度,分支逻辑太多可以考虑使用门面 + 策略的结合模式
public static void main(String[] args) {
String x = "";if(x.equals( "招商银行" )){
System.out.println("逻辑1");}
if(x.equals( "浦发银行" )){
System.out.println("逻辑2");}
if(x.equals( "上海银行" )){
System.out.println("逻辑3");}
if(x.equals( "中国银行" )){
System.out.println("逻辑4");}
if(x.equals( "工商银行" )){
System.out.println("逻辑5");}
}
优化后的代码:
/** * <p>Title:银行处理逻辑抽象定义</p> * <p>Description:</p> * * @author QIQI * @date*/public abstract class AbstractBankAssembleStrategy {
/*
* <p>Title:定义行为的结果,子类实现过程</p>
* <p>Description:</p>
* @author QIQI
* @params []
* @return void
* @throws
* @date 2023/03/15 17:56
*/
public abstract void exe();}
/** * <p>Title:中国银行的实现</p> * <p>Description:</p> * * @author QIQI * @date*/public class ChinaBankAssembleStrategy extends AbstractBankAssembleStrategy{
@Override
public void exe() {
System.out.println("中国银行处理");}
}
/** * <p>Title:上海银行的实现</p> * <p>Description:</p> * * @author QIQI * @date*/public class ShangHaiBankAssembleStrategy extends AbstractBankAssembleStrategy{
@Override
public void exe() {
System.out.println("上海银行处理");}
}
/** * <p>Title:策略的分发器</p> * <p>Description:</p> * * @author QIQI * @date*/public class StrategyContext {
private static HashMap<String,AbstractBankAssembleStrategy> strategyHashMap = new HashMap<>();static {
strategyHashMap.put( "china",new ChinaBankAssembleStrategy() );strategyHashMap.put( "shanghai",new ShangHaiBankAssembleStrategy() );}
public static void strategrExe(String bankName){
strategyHashMap.get( bankName ).exe();}
}
public static void main(String[] args) {
String x = "";StrategyContext.strategrExe( x );}
策略模式结构还是比较简单易懂的,可以看到上面优化完成后已经没有if判断分支的代码了

责任链的使用场景:
1、多个对象可以处理同一个请求,但具体由哪个对象处理则在运行时动态决定。
2、在请求处理者不明确的情况下向对个对象中的一个提交一个请求。
3、需要动态处理一组对象处理请求。
未优化时的代码:
public static void main(String[] args) {
//调用A方法
doTask-A();//如果条件成立调用B方法
if(xx){
doTask-B();}
//在调用C方法
doTask-C();}
优化后的代码:
/** * <p>Title:责任链的抽象定义</p> * <p>Description:</p> * * @author QIQI * @date*/public abstract class AbstractHandler {
@Setter
protected AbstractHandler success;public abstract void doTask();public void next() {
if (success != null) {
success.doTask( );}
}
}
/** * <p>Title:A逻辑实现</p> * <p>Description:</p> * * @author QIQI * @date*/public class TaskAHandler extends AbstractHandler{
@Override
public void doTask() {
System.out.println("执行A逻辑");next();}
}
/** * <p>Title:B逻辑实现</p> * <p>Description:</p> * * @author QIQI * @date*/public class TaskBHandler extends AbstractHandler{
@Override
public void doTask() {
if(xxx){
System.out.println("执行B逻辑");}
next();}
}
/** * <p>Title:C逻辑实现</p> * <p>Description:</p> * * @author QIQI * @date*/public class TaskCHandler extends AbstractHandler{
@Override
public void doTask() {
System.out.println("执行C逻辑");next();}
}
/** * <p>Title:责任链的触发顺序组装</p> * <p>Description:</p> * * @author QIQI * @date*/public class HandlerEvent {
private AbstractHandler aHandler = new TaskAHandler();private AbstractHandler bHandler = new TaskBHandler();private AbstractHandler cHandler = new TaskCHandler();/*
* <p>Title:链式组装类,决定了责任链的固定执行顺序</p>
* <p>Description:</p>
* @author QIQI
* @params []
* @return void
* @throws
* @date 2023/03/15 18:45
*/
public void exe(){
aHandler.setSuccess( bHandler );bHandler.setSuccess( cHandler );cHandler.setSuccess( null );}
}
public static void main(String[] args) {
HandlerEvent handlerEvent = new HandlerEvent();handlerEvent.exe();}

模板模式使用场景:
架构师常用的设计方式,定义最顶层的接口定义,再由不同开发进行实现,最终通过模板在组装起来
业务需求:
鱼可以在水里游
人可以在陆地行走
不同生物都有特有的能力,但是都必须吃饭
未优化时代码:
class Fish{
public void move(){
System.out.println("鱼可以在水里游");}
public void eat(){
System.out.println("鱼需要吃东西");}
//游和吃构建了鱼的生命
public void live(){
move();eat();}
}
class Human{
public void move(){
System.out.println("人可以在陆地走");}
public void eat(){
System.out.println("人需要吃东西");}
//走和吃构建了人的生命
public void live(){
move();eat();}
}
吃是各种生命体的基础条件,每一个live生命都需要eat,如果每一个生命体都这么写代码会很冗余,那么就需要抽象了
优化后的代码:
public abstract class Life {
/*
* <p>Title:行动抽象</p>
* <p>Description:</p>
* @author QIQI
* @params []
* @return void
* @throws
* @date 2023/03/15 19:44
*/
public abstract void move();/*
* <p>Title:吃抽象</p>
* <p>Description:</p>
* @author QIQI
* @params []
* @return void
* @throws
* @date 2023/03/15 19:44
*/
public abstract void eat();/*
* <p>Title:模板,所有子类都必须以此为模板</p>
* <p>Description:加上final为了避免被子类重写,这个是不可改变的基础模板</p>
* @author QIQI
* @params []
* @return void
* @throws
* @date 2023/03/15 19:44
*/
public final void life(){
move();eat();}
}
//鱼的实现
public class Fish extends Life{
@Override
public void move(){
System.out.println("鱼可以在水里游");}
@Override
public void eat(){
System.out.println("鱼需要吃东西");}
}
//人的实现
public class Human extends Life {
@Override
public void move(){
System.out.println("人可以在陆地走");}
@Override
public void eat(){
System.out.println("人需要吃东西");}
}
public static void main(String[] args) {
Life life = new Fish();life.life();}
模板模式就是将多态化的东西抽象成接口交给子类实现,将不可改变的行为定义成模板方法,严格限制子类实现,这样对外展示的life方法内部基础元素都未发生变化,但是又形成了实现的多态化

适配器模式使用场景:
1.系统需要使用现有的类,而此类的接口不符合系统的需要,即接口不兼容
2.想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的一些类一起工作
3.需要一个统一的输出接口,而输入端的接口不可预知
JDBC驱动包就是最常见的适配器模式设计,JDBC协议可以兼容适配mysql、oracle、sqlserver等等数据库
案例场景:
某品牌游戏机的充电器是两相口充电器
某酒店的插座是三相充电口
上面这个场景就会导致游戏机无法充电,那么生活中我们就会去买个转换器来对这种场景进行兼容,那么在代码中其实也是一个道理
模型代码:
/** * <p>Title:游戏机两相充电口的接口定义</p> * <p>Description: * 主要是需要明确定义输出 * </p> * * @author QIQI * @date*/public interface DualPin {
/*
* <p>Title:a/b代表两相口每个口的参数</p>
* <p>Description:两相口为火线、零线</p>
* @author QIQI
* @params [a, b]
* @return void
* @throws
* @date 2023/03/16 9:57
*/
void electrify(int a,int b);}
/** * <p>Title:两相口的游戏机</p> * <p>Description:</p> * * @author QIQI * @date*/public class GameMachines implements DualPin{
@Override
public void electrify(int a, int b) {
System.out.println("两相口的游戏机充电实现");}
}
/** * <p>Title:酒店三相充电口的接口定义</p> * <p>Description: * 主要是需要明确定义输出 * </p> * * @author QIQI * @date*/public interface TriplePin {
/*
* <p>Title:a/b/c代表三相口每个口的参数</p>
* <p>Description:三相口为火线、零线、地线</p>
* @author QIQI
* @params [a, b, c]
* @return void
* @throws
* @date 2023/03/16 9:57
*/
void electrify(int a,int b,int c);}
/** * <p>Title:适配器</p> * <p>Description:</p> * * @author QIQI * @date*/public class Adapter implements TriplePin{
//游戏机两相口接口对象
private DualPin dualPin;/*
* <p>Title:初始化适配器时将两相口的实现放进来</p>
* <p>Description:</p>
* @author QIQI
* @params [dualPin]
* @return
* @throws
* @date 2023/03/16 10:14
*/
public Adapter(DualPin dualPin) {
this.dualPin = dualPin;}
/*
* <p>Title:实现三相口的方法</p>
* <p>Description:</p>
* @author QIQI
* @params [a, b, c]
* @return void
* @throws
* @date 2023/03/16 10:15
*/
@Override
public void electrify(int a, int b, int c) {
//直接忽略三相口的地线参数
dualPin.electrify( a,b );}
}
public static void main(String[] args){
DualPin dualPin = new GameMachines();//两相口游戏机实现传到三相口的适配器
TriplePin triplePin = new Adapter( dualPin );//调用三相口的充电方法
triplePin.electrify( 0,1,2 );}
