网建网站,秦皇岛手机网站,怎么查询公司企业邮箱,5G网站建设前言
虽说使用设计模式可以让复杂的业务代码变得清晰且易于维护#xff0c;但是某些情况下#xff0c;开发可能会遇到我为了简单的业务逻辑去适配设计模式的情况#xff0c;本文笔者就以四种常见的设计模式为例#xff0c;演示如何基于lambda来简化设计模式的实现。
策略…前言
虽说使用设计模式可以让复杂的业务代码变得清晰且易于维护但是某些情况下开发可能会遇到我为了简单的业务逻辑去适配设计模式的情况本文笔者就以四种常见的设计模式为例演示如何基于lambda来简化设计模式的实现。
策略模式
我们的项目中会涉及各种各样的校验可能是校验电话号码、单双数、字符串长度等为此我们希望通过策略模式来封装这些校验规则。
第一步自然是通过接口来定义策略编写一个名为execute方法让用户传入字符串返回校验结果的布尔值
/*** 定义策略模式的接口*/
public interface ValidationStrategy {/*** 校验该字符串是否符合要求若符合则返回true* param str* return*/boolean execute(String str);
}
然后将这个策略接口聚合到我们的校验器中后续我们就可以按照需求传入对应的校验策略即可
/*** 校验工具将策略接口成员成员属性起到依赖抽象的作用*/
public class Validator {private ValidationStrategy strategy;public Validator() {}public Validator(ValidationStrategy strategy) {this.strategy strategy;}public boolean validate(String str) {return strategy.execute(str);}
}
假如我们需要校验这个字符串是否全为字符串小写那么我们就可以封装这样一个类:
/*** 判断是否全为小写*/
public class IsAllLowerCase implements ValidationStrategy {Overridepublic boolean execute(String str) {return str.matches([a-z]);}
}同理如果我们需要判断是否全为数字则可以这样写:
/*** 判断传入字符是否全为数字*/
public class IsNumeric implements ValidationStrategy {Overridepublic boolean execute(String str) {return str.matches(\\d);}
}
使用时我们只需按需传入校验规则即可
public class Main {public static void main(String[] args) {//校验是否全为数字Validator v1 new Validator(new IsNumeric());System.out.println(v1.validate(1234));//校验是否全是小写Validator v2 new Validator(new IsAllLowerCase());System.out.println(v2.validate(dalhl));}
}输出结果如下:
true
true不知道读者是否可以发现问题规则的校验往往只是一两行代码为了适配规则校验所用到的策略模式开发者往往需要对此额外创建一个类要知道字符校验的规则是成百上千的并且很多校验规则很可能仅仅是某个业务才会用到的。
所以我们是否有办法做到既能适配策略模式又避免为了一段简单的校验代码而去创建一个类呢
查看我们校验策略接口ValidationStrategy的定义它要求传入一个String返回一个boolean由此我们想到了java8提供的函数时接口Function其定义如下所示可以根据泛型要求要指明泛型T和Rapply方法要求传入一个T这里可以直接理解为我们的String然后返回一个R同理代入我们的boolean:
FunctionalInterface
public interface FunctionT, R {/*** Applies this function to the given argument.** param t the function argument* return the function result*/R apply(T t);.......}按照java8的lambda语法糖FunctionT, R只有一个需要实现的方法R apply(T t)我们完全可以表面类的创建取而代之的是这样一段表达式:
t-R查看我们ValidationStrategy的定义它也是只有一个方法execute我们完全可以将其视为FunctionString, Boolean即可得表达式s-boolean 由此我们得出下面这段代码可以看到根据接口的定义匹配java8对应的函数时接口然后基于lambda表达式即可完成创建这样做法避免了类的生命避免了简单逻辑复杂化实现的问题
public static void main(String[] args) {//校验是否全为数字Validator v1 new Validator((s) - s.matches(\\d));System.out.println(v1.validate(1234));//校验是否全是小写Validator v2 new Validator(s - s.matches([a-z]));System.out.println(v2.validate(dalhl));}模板方法
银行接待VIP顾客的核心流程为:
查询顾客是否是VIP。招待顾客为顾客办理业务。
所有银行的大体流程都是这样唯一的区别就是第2步对此我们可以使用模板方法模式创建一个抽象类将第1步抽出来而第2步按照不同银行进行不同的实现
public abstract class Banking {public void processCustomer(int id) {//查询会员名String customer getCustomerWithId(id);//招待会员makeCustomerHappy(customer);}private String getCustomerWithId(int id) {return RandomUtil.randomString(5);}protected abstract void makeCustomerHappy(String customer);
}对应两个银行的实现代码,先来看看BankingA 的招待逻辑:
public class BankingA extends Banking {Overrideprotected void makeCustomerHappy(String customer) {System.out.println(请customer吃饭并为其办理业务);}
}BankingB的招待逻辑:
public class BankingB extends Banking {Overrideprotected void makeCustomerHappy(String customer) {System.out.println(请 customer 喝茶并为其办理业务);}
}
测试代码如下
public static void main(String[] args) {BankingA bankingA new BankingA();bankingA.processCustomer(1);BankingB bankingB new BankingB();bankingB.processCustomer(1);}对应输出结果:
请6brkb吃饭并为其办理业务
请autjm喝茶并为其办理业务还是一样的问题找到会员是一段无返回值的简单输出为了适配模板方法这一行代码也还是要创建一个类所以我们还是需要用lambda对其进行简化。
查看抽象方法makeCustomerHappy的定义它要求传入一个传入而返回一个void查阅java8对应的函数式接口我们找到了Consumer
FunctionalInterface
public interface ConsumerT {/*** Performs this operation on the given argument.** param t the input argument*/void accept(T t);于是我们得出公式s-Void 对此我们将抽象类Banking 加以改造将抽象方法makeCustomerHappy改为Consumer接口
public abstract class Banking {public void processCustomer(int id, ConsumerString makeCustomerHappy) {//查询会员名String customer getCustomerWithId(id);//招待会员makeCustomerHappy.accept(customer);}private String getCustomerWithId(int id) {return RandomUtil.randomString(5);}}这样一来后续的调用即可用一段lambda实现
public class Main {public static void main(String[] args) {Banking bankingA new Banking();bankingA.processCustomer(1,customer- System.out.println(请customer吃饭并为其办理业务));Banking bankingB new Banking();bankingB.processCustomer(1,customer- System.out.println(请customer喝茶并为其办理业务));}
}观察者模式
观察者模式算是最经典也最好理解的设计模式观察者只需将自己注册到感兴趣的主题上一旦有主题更新就会及时通知观察者观察者按照自己的需要进行响应处理。
对此我们首先定义观察者的接口:
/*** 观察者*/
public interface Observer {void inform(String msg);
}
接下来就是主题:
public interface Subject {void registerObserver(Observer observer);void notifyObserver();
}
创建一个观察者1以及观察者2以及实现他们对自己感兴趣主题时会做出的反馈输出方法inform:
public class Observer1 implements Observer {Overridepublic void inform(String msg) {System.out.println(观察者1收到通知内容为 msg);}
}
public class Observer2 implements Observer {Overridepublic void inform(String msg) {System.out.println(观察者2收到通知内容为 msg);}
}
最后就是主题类的实现我们将观察者聚合如果观察者对SubJect1 感兴趣则通过registerObserver完成注册一旦主题要发布新消息就可以通过notifyObserver及时通知每一个订阅者:
public class SubJect1 implements Subject {private String msg;public SubJect1(String msg) {this.msg msg;}private ListObserver observerList new ArrayList();Overridepublic void registerObserver(Observer observer) {observerList.add(observer);}Overridepublic void notifyObserver() {observerList.forEach(o - o.inform(msg));}
}
测试代码和对应输出结果如下所示
public static void main(String[] args) {SubJect1 subJect1 new SubJect1(请大家学习《基于lambda简化设计模式》);//注册订阅者subJect1.registerObserver(new Observer1());subJect1.registerObserver(new Observer2());//主题发起通知subJect1.notifyObserver();}输出结果:
观察者1收到通知内容为请大家学习《基于lambda简化设计模式》
观察者2收到通知内容为请大家学习《基于lambda简化设计模式》很明显的Observer的inform是典型的Consumer接口我们直接将其简化
public static void main(String[] args) {SubJect1 subJect1 new SubJect1(请大家学习《基于lambda简化设计模式》);//注册订阅者subJect1.registerObserver(s - System.out.println(观察者1收到消息 s));subJect1.registerObserver(s - System.out.println(观察者2收到消息 s));//主题发起通知subJect1.notifyObserver();}责任链模式
我们希望字符串被对象1处理完成之后要转交给对象2处理并且我们后续可能还会交给更多的对象处理通过对需求的梳理和抽象这个功能完全可以通过责任链模式来实现。
首先声明公共抽象类可以看到考虑类的通用性笔者将这个类的入参设置为泛型并且公共方法handle的步骤为:
调用自己的handWork处理输入数据handWork交给实现类自行编写。若successor不为空则将处理结果交给下一个处理器处理由此构成一条处理链。
public abstract class ProcessingObjectT {/*** 下一个处理器*/private ProcessingObjectT successor;public ProcessingObjectT getSuccessor() {return successor;}public void setSuccessor(ProcessingObjectT successor) {this.successor successor;}public T handle(T input) {//先自己处理完如果有后继责任链则交给后面的责任链处理递归下去T t handWork(input);if (successor ! null) {return successor.handWork(t);}return t;}/*** 自己的处理逻辑** param intput* return*/abstract T handWork(T intput);
}对应的我们基于这个抽象类实现两个字符处理器ProcessingStr1会将收到的中文逗号换位英文逗号
public class ProcessingStr1 extends ProcessingObjectString {OverrideString handWork(String intput) {return intput.replace(, ,);}
}而ProcessingStr2 会将中文句号替换为英文句号
public class ProcessingStr2 extends ProcessingObjectString {OverrideString handWork(String intput) {return intput.replace(。, .);}
}测试代码和输出结果如下
public static void main(String[] args) {ProcessingObjectString p1 new ProcessingStr1();ProcessingObjectString p2 new ProcessingStr2();p1.setSuccessor(p2);System.out.println(p1.handle(helloworld。));}可以看到所有的中文符号都被替换成英文符号了:
hello,world.话不多说不难看出上文这种传入String返回String的方法我们完全可以使用UnaryOperator函数式接口实现表达式。
从UnaryOperator源码可知它继承Function我们只需传入泛型T即可得到一个FunctionT, T从而让我们得到一个T-T的Function表达式
FunctionalInterface
public interface UnaryOperatorT extends FunctionT, T {/*** Returns a unary operator that always returns its input argument.** param T the type of the input and output of the operator* return a unary operator that always returns its input argument*/static T UnaryOperatorT identity() {return t - t;}
}而责任连的方式也很简单因为UnaryOperator是Function的子类这意味着我们可以使用Function的andThen将所有的UnaryOperator完成衔接 UnaryOperatorString p1 i - i.replace(, ,);UnaryOperatorString p2 i - i.replace(。, .);p1.andThen(p2);System.out.println(p1.apply(helloworld。));小结
为了适配设计模式常会出现为了一段简单的逻辑而去编写大量实现类的情况所以我们建议对于逻辑比较简单且需要适配设计模式的功能可以尝试找到合适的函数式接口简化功能的实现避免大量类文件的声明。
参考
Java 8 in Action