目录
前言
Java程序设计模式是为了解决特定问题而开发的一系列重复使用的代码设计方法。以下是几种常见的Java程序设计模式及其使用:
-
工厂模式:将对象的创建与使用分离,使得代码更具有可读性和可维护性。
-
单例模式:保证一个类仅有一个实例,并提供一个全局访问点,避免重复创建对象,提高程序性能。
-
观察者模式:定义对象之间的一对多依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象都得到通知并自动更新。
-
建造者模式:将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。
-
适配器模式:将一个类的接口转换成客户希望的另一个接口,使得原本不兼容的类可以一起工作。
-
模板方法模式:定义一个算法的骨架,将具体实现延迟到子类中,使得子类可以在不改变算法结构的情况下重新定义算法的某些步骤。
-
策略模式:定义一系列算法,将每个算法封装起来并使它们可以互换,使得算法的变化独立于使用它们的客户端。
-
装饰器模式:动态地将责任添加到对象上,是一种替代继承的技术,使得不必创建子类就可以扩展对象的功能。
这些设计模式都是经过实践验证过的有效方法,能够提高程序的可读性、可维护性、可扩展性和可重用性。在Java程序设计中,根据需要选择合适的设计模式可以大大提高程序开发效率和质量。
我们在日常开发中,经常会遇到类似的场景:当要做一件事儿的时候,这件事儿的步骤是固定好的,但是每一个步骤的具体实现方式是不一定的。通常,遇到这种情况,我们会把所有要做的事儿抽象到一个抽象类中,并在该类中定义一个模板方法。这就是所谓的模板方法模式。
以前的模板方法
当我们平常点外卖的时候需要以下步骤:1.外卖平台、2.点外卖、3.评价。
三个步骤中外卖平台和评价都是固定的流程,每个人要做的事基本都一样。但是点外卖这个步骤根据每个人喜欢吃的爱好而选择不同。
我们可以将整个办业务这件事儿封装成一个抽象类:
public abstract class AbstractBusinessHandler {
/**
* 模板方法
*/
public final void execute(){
getDeliveryPlatform();
chooseFood();
judge();
}
/**
* 打开外卖平台
* @return
*/
private void getDeliveryPlatform(){
System.out.println("打开了美团");
}
/**
* 点外卖
*/
public abstract void chooseFood(); //抽象的的美食方法,由子类实现
/**
* 评价
*/
private void judge(){
System.out.println("我点的外卖太好吃了");
}
}
我们在类中定义了一个execute类,这个类编排了getDeliveryPlatform、chooseFood和judge三个方法。这就是一个模板方法。
其中getDeliveryPlatform和judge都有通用的实现,只有chooseFood方法是个抽象的,需要子类根据实际要办的业务的内容去重写。
有了这个抽象类和模板方法,当我们想要实现一个"打车"的时候,只需要继承该AbstractBusinessHandeler并且重写chooseFood方法即可:
public class TaxiBusiness extends AbstractBusinessHandeler {
@Override
public void handle() {
System.out.println("欢迎使用美团打车");
}
}
这样,我们在执行打车业务逻辑的时候,只需要调用 SaveMoneyHandler的execute方法即可:
public static void main(String []args){
SaveMoneyHandler saveMoneyHandler = new SaveMoneyHandler();
saveMoneyHandler.execute();
}
输出结果:
模版设计方法实践
打开了美团
欢迎使用美团打车
我点的外卖太好吃了
以上,就是一个简单的模板方法的实现。通过使用模板方法,可以帮助我们很大程度的复用代码。
因为我们要在外卖平台有很多业务,所以可能需要定义很多的实现类:
//理发业务的实现类
public class HairdressingBusiness extends AbstractBusinessHandeler {
@Override
public void handle() {
System.out.println("我点开了理发");
}
}
//旅游业务的实现类
public class TravelBusiness extends AbstractBusinessHandeler{
@Override
public void handle() {
System.out.println("我点开了旅游");
}
}
一直以来,开发者们在使用模板方法的时候基本都是像上面这个例子一样:需要准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来让子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。
但是,有了Java 8以后,模板方法有了另外一种实现方式,不需要定义特别多的实现类了。
Java 8 的函数式编程
2014年,Oracle发布了 Java 8,在Java 8中最大的新特性就是提供了对函数式编程的支持。
Java 8在java.util.function下面增加增加一系列的函数接口。其中主要有Consumer、Supplier、Predicate、Function等。
本文主要想要介绍一下Supplier和Consumer这两个,使用者两个接口,可以帮我们很好的改造模板方法。这里只是简单介绍下他们的用法,并不会深入展开,如果大家想要学习更多用法,可以自行google一下。
Supplier
Supplier是一个供给型的接口,简单点说,这就是一个返回某些值的方法。
最简单的一个Supplier就是下面这段代码:
public List<String> getList() {
return new ArrayList();
}
使用Supplier表示就是:
Supplier<List<String>> listSupplier = ArrayList::new;
Consumer
Consumer 接口消费型接口,简单点说,这就是一个使用某些值(如方法参数)并对其进行操作的方法。
最简单的一个Consumer就是下面这段代码:
public void sum(String a1) {
System.out.println(a1);
}
使用Consumer表示就是:
Consumer<String> printConsumer = a1 -> System.out.println(a1);
Consumer的用法,最见的的例子就是是Stream.forEach(Consumer)这样的用法,
它接受一个Consumer,该Consumer消费正在迭代的流中的元素,并对每个元素执行一些操作,比如打印:
Consumer<String> stringConsumer = (s) -> System.out.println(s.length());
Arrays.asList("ab", "abc", "a", "abcd").stream().forEach(stringConsumer);
Java 8以后的模板方法
在介绍过了Java 8中的Consumer、Supplier之后,我们来看下怎么改造之前我们介绍过的模板方法。这里面采用银行办理业务流程实现。
首先,我们定义一个BankBusinessHandler类,并且重新定义一个execute方法,这个方法有一个入参,是Consumer类型的,然后移除handle方法,重新编排后的模板方法内容如下:
public class BankBusinessHandler {
private void execute(Consumer<BigDecimal> consumer) {
getNumber();
consumer.accept(null);
judge();
}
private void getNumber() {
System.out.println("number-00" + RandomUtils.nextInt());
}
private void judge() {
System.out.println("give a praised");
}
}
我们实现的模板方法execute中,编排了getNumber、judge以及consumer.accept,这里面consumer.accept就是具体的业务逻辑,可能是存钱、取钱、理财等。需要由其他方法调用execute的时候传入。
这时候,我们想要实现"存钱"业务的时候,需要BankBusinessHandler类中增加以下方法:
public class BankBusinessHandler {
public void save(BigDecimal amount) {
execute(a -> System.out.println("save " + amount));
}
}
在save方法中,调用execute方法,并且在入参处传入一个实现了"存钱"的业务逻辑的Comsumer。
这样,我们在执行存钱的业务逻辑的时候,只需要调用 BankBusinessHandler的save方法即可:
public static void main(String[] args) throws {
BankBusinessHandler businessHandler = new BankBusinessHandler();
businessHandler.save(new BigDecimal("1000"));
}
输出结果:
number-001736151440
save1000
give a praised
如上,当我们想要实现取钱、理财等业务逻辑的时候,和存钱类似:
public class BankBusinessHandler {
public void save(BigDecimal amount) {
execute(a -> System.out.println("save " + amount));
}
public void draw(BigDecimal amount) {
execute(a -> System.out.println("draw " + amount));
}
public void moneyManage(BigDecimal amount) {
execute(a -> System.out.println("draw " + amount));
}
}
可以看到,通过使用Java 8中的Comsumer,我们把模板方法改造了,改造之后不再需要抽象类、抽象方法,也不再需要为每一个业务都创建一个实现类了。我们可以把所有的业务逻辑内聚在同一个业务类中。这样非常方便这段代码的后期运维。
前面介绍如何使用Consumer进行改造模板方法,那么Supplier有什么用呢?
我们的例子中,在取号、办业务、评价这三个步骤中,办业务是需要根据业务情况进行定制的,所以,我们在模板方法中,把办业务这个作为扩展点开放给外部。
有这样一种情况,那就是现在我们办业务的时候,取号的方式也不一样,可能是到银行网点取号、在网上取号或者银行客户经理预约的无需取号等。
无论取号的方式如何,最终结果都是取一个号;而取到的号的种类不同,可能接收到的具体服务也不同,比如vip号会到VIP柜台办理业务等。
想要实现这样的业务逻辑,就需要使用到Supplier,Supplier是一个"供给者",他可以用来定制"取号逻辑"。
首先,我们需要改造下模板方法:
public class BankBusinessHandler {
public void execute(Supplier<String> supplier, Consumer<BigDecimal> consumer) {
String number = supplier.get();
System.out.println(number);
if (number.startsWith("vip")) {
//Vip号分配到VIP柜台
System.out.println("Assign To Vip Counter");
} else if (number.startsWith("reservation")) {
//预约号分配到专属客户经理
System.out.println("Assign To Exclusive Customer Manager");
} else {
//默认分配到普通柜台
System.out.println("Assign To Usual Manager");
}
consumer.accept(null);
judge();
}
public void getNumber() {
System.out.println("number-00" + System.currentTimeMillis());
}
public void judge() {
System.out.println("give a praised");
}
}
/**
* 当我们想要实现取钱、理财等业务逻辑的时候,和存钱类似:
*/
public class BusinessAbstraction extends BankBusinessHandler{
public void saveVip(BigDecimal amount) {
execute(() -> "vipNumber-00" + System.currentTimeMillis(), a -> System.out.println("save " + amount));
}
public void save(BigDecimal amount) {
execute(() -> "number-00" + System.currentTimeMillis(), a -> System.out.println("save " + amount));
}
public void saveReservation(BigDecimal amount) {
execute(() -> "reservationNumber-00" + System.currentTimeMillis(), a -> System.out.println("save " + amount));
}
}
public class Main {
public static void main(String[] args) {
System.out.println("模版设计方法实践");
BusinessAbstraction businessHandler = new BusinessAbstraction();
businessHandler.saveVip(new BigDecimal("1000"));
}
}
总结
本文中的例子实现过程比较简单,但是我们的真实业务中的逻辑讲起来又比较复杂。
所以,这就需要大家能够多多理解并且实践一下。如果你代码中用到过模板方法模式,那一定是可以通过本文中的方法进行改造的。
如果你还没用过模板方法模式,那说明你的应用中一定有很多重复代码,那就赶紧用起来。
没有最好的代码,只有更好的代码!