常用设计模式的正确应用
创始人
2025-05-28 22:40:53
0

原型模式使用场景

细胞分列式的New出大量重复对象

未优化时代码:

开发代码中常见的循环赋值装载进集合的代码,这段代码在大多数场景下是没问题的,但是在要求高性能的场景下可能存在性能不好的情况

代码逻辑其实很简单就是new对象赋值装载到List中

public class BokeTest {public static void main(String[] args){List 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;//实现浅拷贝@Overrideprotected User clone() throws CloneNotSupportedException {return (User) super.clone();}
}
/** * 

Title:用户对象生成工厂

*

Description:

* * @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 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;}
/*
* 

Title:根据传入的口红品牌给女孩上口红

*

Description:

* @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());} /* *

Title:根据传入的口红品牌给女孩上口红

*

Description:

* @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(); }

上面的代码因为二期的迭代在不同的私有方法中都加入了新的实现以及最终加了一个是否加分的判断逻辑,如果继续迭代下去功能越来越复杂此时代码会很难维护(修改一个小逻辑可能影响全局判断)

优化后的代码:

/** * 

Title:化妆功能基类接口

*

Description: * 抽象基础功能接口 *

* * @author QIQI * @date*/public interface DecoatorI {/**

Title:化妆完成后展示定义

*

Description:

* @author QIQI* @params []* @return void* @throws* @date 2023/03/14 20:28*/void show();} /** *

Title:化妆装饰抽象装饰类

*

Description:

* * @author QIQI * @date*/public abstract class Decoator implements DecoatorI {DecoatorI decoatorI;public Decoator(DecoatorI decoatorI) {this.decoatorI = decoatorI;}@Overridepublic void show() {decoatorI.show();} } /** *

Title:抽象实现,普通场景的show实现

*

Description:

* * @author QIQI * @date*/public class CommonShow implements DecoatorI {@Overridepublic void show() {System.out.println("普通展示");} } public interface RougeService{/**

Title:口红接口定义

*

Description:

* @author QIQI* @params [rougeBrand]* @return void* @throws* @date 2023/03/14 15:39*/void rouge(Rouge rouge);} /***

Title:口红定义,不同品牌不同子类实现

*

Description:

* @author QIQI* @date * */public interface RougeService{/**

Title:口红接口定义

*

Description:

* @author QIQI* @params [rougeBrand]* @return void* @throws* @date 2023/03/14 15:39*/void rouge(Rouge rouge);} /** *

Title:X品牌口红实现

*

Description:

* * @author QIQI * @date*/public class BrandXRougeServiceImpl extends Decoator implements RougeService{private Rouge rouge;public BrandXRougeServiceImpl(DecoatorI decoatorI,Rouge rouge) {super( decoatorI );this.rouge = rouge;}@Overridepublic void show() {//用了品牌X的口红rouge(rouge);//展现状态decoatorI.show();}@Overridepublic 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");}
}

优化后的代码:

/** * 

Title:银行处理逻辑抽象定义

*

Description:

* * @author QIQI * @date*/public abstract class AbstractBankAssembleStrategy {/**

Title:定义行为的结果,子类实现过程

*

Description:

* @author QIQI* @params []* @return void* @throws* @date 2023/03/15 17:56*/public abstract void exe();} /** *

Title:中国银行的实现

*

Description:

* * @author QIQI * @date*/public class ChinaBankAssembleStrategy extends AbstractBankAssembleStrategy{@Overridepublic void exe() {System.out.println("中国银行处理");} } /** *

Title:上海银行的实现

*

Description:

* * @author QIQI * @date*/public class ShangHaiBankAssembleStrategy extends AbstractBankAssembleStrategy{@Overridepublic void exe() {System.out.println("上海银行处理");} } /** *

Title:策略的分发器

*

Description:

* * @author QIQI * @date*/public class StrategyContext {private static HashMap 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();}

优化后的代码:

/** * 

Title:责任链的抽象定义

*

Description:

* * @author QIQI * @date*/public abstract class AbstractHandler {@Setterprotected AbstractHandler success;public abstract void doTask();public void next() {if (success != null) {success.doTask( );}} } /** *

Title:A逻辑实现

*

Description:

* * @author QIQI * @date*/public class TaskAHandler extends AbstractHandler{@Overridepublic void doTask() {System.out.println("执行A逻辑");next();} } /** *

Title:B逻辑实现

*

Description:

* * @author QIQI * @date*/public class TaskBHandler extends AbstractHandler{@Overridepublic void doTask() {if(xxx){System.out.println("执行B逻辑");}next();} } /** *

Title:C逻辑实现

*

Description:

* * @author QIQI * @date*/public class TaskCHandler extends AbstractHandler{@Overridepublic void doTask() {System.out.println("执行C逻辑");next();} } /** *

Title:责任链的触发顺序组装

*

Description:

* * @author QIQI * @date*/public class HandlerEvent {private AbstractHandler aHandler = new TaskAHandler();private AbstractHandler bHandler = new TaskBHandler();private AbstractHandler cHandler = new TaskCHandler();/**

Title:链式组装类,决定了责任链的固定执行顺序

*

Description:

* @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 {/** 

Title:行动抽象

*

Description:

* @author QIQI* @params []* @return void* @throws* @date 2023/03/15 19:44*/public abstract void move();/**

Title:吃抽象

*

Description:

* @author QIQI* @params []* @return void* @throws* @date 2023/03/15 19:44*/public abstract void eat();/**

Title:模板,所有子类都必须以此为模板

*

Description:加上final为了避免被子类重写,这个是不可改变的基础模板

* @author QIQI* @params []* @return void* @throws* @date 2023/03/15 19:44*/public final void life(){move();eat();} } //鱼的实现 public class Fish extends Life{@Overridepublic void move(){System.out.println("鱼可以在水里游");}@Overridepublic void eat(){System.out.println("鱼需要吃东西");} } //人的实现 public class Human extends Life {@Overridepublic void move(){System.out.println("人可以在陆地走");}@Overridepublic 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等等数据库

案例场景:

某品牌游戏机的充电器是两相口充电器
某酒店的插座是三相充电口

上面这个场景就会导致游戏机无法充电,那么生活中我们就会去买个转换器来对这种场景进行兼容,那么在代码中其实也是一个道理

模型代码:

/** * 

Title:游戏机两相充电口的接口定义

*

Description: * 主要是需要明确定义输出 *

* * @author QIQI * @date*/public interface DualPin {/**

Title:a/b代表两相口每个口的参数

*

Description:两相口为火线、零线

* @author QIQI* @params [a, b]* @return void* @throws* @date 2023/03/16 9:57*/void electrify(int a,int b);} /** *

Title:两相口的游戏机

*

Description:

* * @author QIQI * @date*/public class GameMachines implements DualPin{@Overridepublic void electrify(int a, int b) {System.out.println("两相口的游戏机充电实现");} } /** *

Title:酒店三相充电口的接口定义

*

Description: * 主要是需要明确定义输出 *

* * @author QIQI * @date*/public interface TriplePin {/**

Title:a/b/c代表三相口每个口的参数

*

Description:三相口为火线、零线、地线

* @author QIQI* @params [a, b, c]* @return void* @throws* @date 2023/03/16 9:57*/void electrify(int a,int b,int c);} /** *

Title:适配器

*

Description:

* * @author QIQI * @date*/public class Adapter implements TriplePin{//游戏机两相口接口对象private DualPin dualPin;/**

Title:初始化适配器时将两相口的实现放进来

*

Description:

* @author QIQI* @params [dualPin]* @return* @throws* @date 2023/03/16 10:14*/public Adapter(DualPin dualPin) {this.dualPin = dualPin;}/**

Title:实现三相口的方法

*

Description:

* @author QIQI* @params [a, b, c]* @return void* @throws* @date 2023/03/16 10:15*/@Overridepublic 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 );}

相关内容

热门资讯

亿利达:诉讼事项进展 金融界4月23日消息,亿利达公告称,公司于近日收到安徽省合肥市包河区人民法院送达的(2022)皖 0...
菲媒:菲律宾副总统称,不优先考... 【环球网报道】综合菲律宾《马尼拉标准报》等媒体6月1日报道,菲律宾副总统莎拉·杜特尔特称,她不优先考...
原创 美... 特朗普再次执掌白宫后,他的“地盘扩张梦”可谓是雷声大雨点小,搞得沸沸扬扬却未见实效。他本想一口气吞掉...
法网-郑钦文鏖战2-1萨姆索诺... 北京时间6月1日,2025赛季网球大满贯法国公开赛继续进行,在女单第三轮的一场比赛中,赛会8号种子、...
以国防军:黎以停火以来超180... △黎巴嫩南部地区(资料图) 以色列国防军当地时间6月1日下午发布消息称,当天上午,一名黎巴嫩真主党特...
深夜,巨子生物突发声明:接受检... 每经编辑|金冥羽 巨子生物旗下重组胶原蛋白品牌可复美产品成分争议持续发酵。 6月1日22点32分,...
新修订的《快递暂行条例》6月1... 6月1日起,《国务院关于修改〈快递暂行条例〉的决定》正式施行。此次修改,专门增加了“快递包装”章节,...
开放“以债换房”政策,可直接置... “南京网络辟谣”微信公众号6月1日发文称,近日,有“南京二手房零首付李经理”“合肥瑶珺房地产代理有限...
GCN的几种模型复现笔记 引言 本篇笔记紧接上文,主要是上一篇看写了快2w字,再去接入代码感觉有点...