赞
踩
官方定义:应该有且仅有一个原因引起类的变更。
单一职责适用于类、接口、方法,比如:比如一个方法尽量只做一件事情。
优点:提高可读性、可维护性。
总结:"职责"没有一个量化的标准、一个类到底要负责哪些职责?职责怎么去细化需要根据项目的实际去考虑,
比如项目的可变因素和不可变因素,项目的收益成本比。
接口一定要做到单一职责、实现类建议多方面考虑,尽量做到只有一个原因引起的变化,过分细分类的职责也会增加系统的复杂性。
官方定义:所有使用基类的地方必须能透明的使用其子类对象
解释:只要父类能出现的地方子类就能出现,且替换子类也不会产生任何错误或异常,使用者可能根本就不需要知道使用的是父类还是子类,但是反过来就不行。
总结:采用里氏替换原则时,尽量避免子类的“个性“。里氏替换原则为继承定义了如下规范:
1.子类必须完全实现父类的方法。
2.子类可以有自己的个性
3.子类覆盖父类的方法时参数可以放大(不是overrivde而是overload)
官方定义:高层模块不应该依赖低层模块,两者都应该依赖抽象。抽象不应该依赖细节。细节应该依赖抽象。
解释:在JAVA中,这个定义就是
1.模块间的依赖通过抽象发生,具体实现类不直接发生依赖,依赖关系通过接口和抽象类产生。
2.接口或实现类不依赖具体实现类。
3.实现类依赖接口或抽象类。
简而言之:面向接口编程。
总结:依赖倒置理论很好的解释了“面向接口编程”的思想,依赖倒置的三种写法:
1.构造函数传递依赖对象
2.Setter方法传递依赖对象
3.接口声明依赖对象(通过方法参数传递)
官方定义:1.客户端不应该依赖他不需要的接口,2.类之间的依赖关系应该建立在最小的接口上。
解释:个人简单理解为:1.不需要的接口不要注入进来,2.接口细化,依赖的接口方法尽量少,不需要的方法也不要提供给我调用。
总结:接口隔离即是接口的定义,也是对类的定义,接口和类尽量使用原子类或原子接口来组装。接口设计的颗粒度越小,系统越灵活,
但是灵活的同时也带来了结构的复杂化,接口的设计一定要注意一个"度",这个”度“没有一个固化或可测量的标准,可以参考
如下原则:
1.一个接口只服务于一个模块或一个业务逻辑;
2.通过业务逻辑压缩接口中的public方法,尽量让接口”满身筋骨肉“,而不是”肥嘟嘟“的一大堆方法。
3.已经被污染的接口,尽量去修改,如果修改风险比较大,可以采用适配器模式去转换。
官方定义:迪米特法则又称最少知道原则。(Only talk to your immedate friends,只跟直接的朋友通信)。
解释:出现在成员变量、方法的输入输出参数中的类称为成员朋友类,而出现在方法体内的类不属于朋友类;
如老师让体育委员清点女生的例子,老师跟女生就不是之直接朋友)
总结:迪米特法的核心观点是类之间的解耦则归纳为一下几点:
1.只和朋友交流
2.朋友之间也有距离(刺猬取暖)?
3.自己的就是自己的;如果有一个方法放在哪个类感觉都可以就可以使用如下原则:放在本类既不增加类关系也不会产生负面影响。
官方定义:一个类或方法应该对扩展开发,对修改关闭。
解释:软件只要在生命周期内都会发送变化,在设计时尽量适应这些变化,以提高项目的稳定性和灵活性。
总结:开闭原则告诉我们尽量通过扩展实体(类)的行为,而不是通过修改已有的代码来完成。
将一个类的接口变成客户端期望的另一种接口
- 已经存在的类,他的方法和需求不匹配,
- 适配器模式不是软件设计阶段考虑的类,是由于随着软件的发展,不同产品,不同厂家功能类似、而接口不同的情况的解决方案。
总结:
适配器模式的局限性在于
1.原功能与期望的功能的输入参数和返回值要相同,即使不同也要有固定的关联关系
。
就像适配器经典例子中的电压一样,输入都是插头,输出都是电流。
类适配器采用继承的方式实现,暴漏了原方法给客户端,违法了最小职责原则,使用对象适配器能解决这一问题。
//原始类(已经存在的功能)
public class Origin {
//已经存在的方法
public int exitsMethod(){
return 1;
}
}
//目标类(开发的新功能)
public interface Target {
// 开发的新方法
public Boolean expectMethod(String args);
}
//测试
public class Test {
public static void main(String[] args) {
Adapter adapter = new Adapter();
Boolean abc = adapter.expectMethod("abc");
int i = adapter.exitsMethod(); //两个方法都暴漏给了客户端,不合适
}
}
// 适配器
public class Adapter extends Origin implements Target {
@Override
public Boolean expectMethod(String args) {
return exitsMethod() == 1;
}
}
类图:
代码:
//原始类(已经存在的功能)
public class Origin {
//已经存在的方法
public int exitsMethod(){
return 1;
}
}
//目标类(开发的新功能)
public interface Target {
// 开发的新方法
public Boolean expectMethod(String args);
}
// 适配器
public class Adapter implements Target {
private Origin origin;
public Adapter(Origin origin) {
this.origin = origin;
}
@Override
public Boolean expectMethod(String args) {
return origin.exitsMethod() == 1;
}
}
//测试
public class Test {
public static void main(String[] args) {
Adapter adapter = new Adapter(new Origin());
Boolean abc = adapter.expectMethod("abc");
}
}
类图:
需求:
现有登录方式:
- 用户名密码登录
需要新增如下第三方登录功能- 手机号验证码登录
- 微信登录
需要实现不同的厂商的同一登录功能。
//============= 现有功能代码===start=====
//现有的用户名密码登录
public class PasswordLoginService {
// 注册
public Boolean register(String userId,String password){
return true;
}
//用户密码登录
public Boolean login(String userId,String password){
return true;
}
}
//============= 现有功能代码===end=====
//新增第三方登录接口
public interface ThirdPartyLogin {
/** 手机号密码登录*/
Boolean loginByPhone(String phone,String code);
/**微信登录*/
Boolean loginWechat(String openId);
}
//适配器
public class PasswordForThirdPartyLoginAdapter extends PasswordLoginService implements ThirdPartyLogin{
@Override
public Boolean loginByPhone(String phone, String code) {
//缺少调用运营商校验验证码是否输入正确
return this.loginForRegister(phone,null);
}
@Override
public Boolean loginWechat(String openId) {
//缺少调用微信平台,校验openId
return this.loginForRegister(openId,null);
}
private Boolean loginForRegister(String userId,String password){
if(password == null){
password = "第三方登录,不需要密码";
}
if(super.register(userId,password)){
super.login(userId,password);
}
return true;
}
}
//测试
public class Test {
public static void main(String[] args) {
PasswordForThirdPartyLoginAdapter adapter = new PasswordForThirdPartyLoginAdapter();
adapter.login("用户命密码登录","密码");
adapter.loginByPhone("13822992932","372652");
adapter.loginWechat("wechatopenid");
}
}
版本一有一些缺陷
适配器中缺少第三方的一些校验代码逻辑,实际开发中如果将对接第三方的代码写在这个适配器中将会变得十分臃肿。
// 原有的密码登录
public class PasswordLoginService {
// 注册
public Boolean register(String userId,String password){
return true;
}
//用户密码登录
public Boolean login(String userId,String password){
return true;
}
}
public interface ThirdPartyLogin {
/** 手机号密码登录*/
Boolean loginByPhone(String phone,String code);
/**微信登录*/
Boolean loginWechat(String openId);
}
//登录适配器接口,所有的第三方登录实现改接口
public interface LoginAdapter {
Boolean support(LoginAdapter adapter);
Boolean login(String userId,LoginAdapter adapter);
}
//适配器(仅转接,不承接业务)
public class PasswordForThirdPartyLoginAdapter extends PasswordLoginService implements ThirdPartyLogin {
@Override
public Boolean loginByPhone(String phone, String code) {
return handleLogin(phone,new PhoneLoginAdapter());
}
@Override
public Boolean loginWechat(String openId) {
return handleLogin(openId,new WechatLoginAdapter());
}
private Boolean handleLogin(String userId,LoginAdapter loginAdapter){
if(loginAdapter.support(loginAdapter)){
return loginAdapter.login(userId,loginAdapter);
}
return false;
}
}
// 抽象登录适配器,作用:抽取公共的注册登录方法
public abstract class AbstractLoginAdapter extends PasswordLoginService implements LoginAdapter {
protected Boolean loginForRegister(String userId,String password){
if(password == null){
password = "第三方登录,不需要密码";
}
if(super.register(userId,password)){
super.login(userId,password);
}
return true;
}
}
//手机登录适配器
public class PhoneLoginAdapter extends AbstractLoginAdapter {
@Override
public Boolean support(LoginAdapter adapter) {
return adapter instanceof PhoneLoginAdapter;
}
@Override
public Boolean login(String userId, LoginAdapter adapter) {
return super.loginForRegister(userId,null);
}
}
//微信登录适配器
public class WechatLoginAdapter extends AbstractLoginAdapter {
@Override
public Boolean support(LoginAdapter adapter) {
return adapter instanceof WechatLoginAdapter;
}
@Override
public Boolean login(String userId, LoginAdapter adapter) {
return super.loginForRegister(userId,null);
}
}
//测试
public class Test {
public static void main(String[] args) {
PasswordForThirdPartyLoginAdapter adapter = new PasswordForThirdPartyLoginAdapter();
adapter.login("用户命密码登录","密码");
adapter.loginByPhone("13822992932","372652");
adapter.loginWechat("wechatopenid");
}
}
委派模式不属于GOF23种设计模式,是一种行为模式,与门面模式类似,不同的是委派模式是运行中
的行为处理逻辑。
委派模式更加注重委派的逻辑,而门面模式是一开始就定义好了的委托对象。
例如一个领导有很多下属,领导会跟进下属熟悉的领域进行安排工作,老板本身不参加具体事务。
委派模式能够将一个大型的任务拆分,更够通过管理子任务的执行情况解耦和提升效率。
委派模式与代理模式也有很多相似之处,区别如下:
代码
//员工接口
public interface Employee {
void doSomething(String task);
}
public class EmployeeA implements Employee{
@Override
public void doSomething(String task) {
System.out.println("我擅长写报告...");
}
}
public class EmployeeB implements Employee{
@Override
public void doSomething(String task) {
System.out.println("我擅长公关...");
}
}
// 领导跟进员工的特征安排任务
public class Leader implements Employee {
@Override
public void doSomething(String task) {
if("接待".equals(task)){
new EmployeeB().doSomething(task);
} else if("汇报".equals(task)){
new EmployeeA().doSomething(task);
}
}
}
// 测试,老板发话给领导安排任务,领导找到员工
public class Boos {
public void command(String task,Leader leader){
leader.doSomething(task);
}
public static void main(String[] args) {
new Boos().command("接待",new Leader());
}
}
类图:
是一种将数据结构与数据操作分离的设计模式
例如软件公司有若干码农,他们有一个共同的数据结构(姓名,KPI,bug数量,开发的功能,代码行数),对于不同的领导,如直属领导关注开发的功能,代码行数,开发部门经理关注程序员的bug数,公司CEO关注码农的KPI。
结合例子解释概念:
程序员的属性就是一个稳定的数据结构,公司的不同领导就是不同的访问者,他们关注的点(操作)各不相同。
- 数据结构稳定、作用与数据结构的操作经常变化的操作
- 需要数据结构和数据操作分离的场景
#(一 员工抽象)
public abstract class Employee {
//员工姓名
private String name;
//kpi分数
private int kpi;
public Employee(String name, int kpi) {
this.name = name;
this.kpi = kpi;
}
# 提供一个访问者进入的接口
public abstract void accept(Visitor visitor);
}
#(二 程序员实现)
public class Coder extends Employee {
public Coder(String name, int kpi) {
super(name, kpi);
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
/**
* 产生的bug数
*/
public Integer bugNumbers(){
return new Random().nextInt(100);
}
}
#(三 产品经理实现)
public class Manager extends Employee{
public Manager(String name, int kpi) {
super(name, kpi);
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
/**
* 经理考核上线的系统数量
* @return
*/
public int systemCount(){
return new Random().nextInt(10);
}
}
# 访问者接口
public interface Visitor {
// 访问员工
void visit(Coder employee);
* 访问经理
*/
void visit(Manager employee);
}
# 访问者实现一:CTO
public class CTOVisitor implements Visitor {
@Override
public void visit(Coder employee) {
System.out.println("CTO看"+employee.getName()+"的bug数"+employee.bugNumbers());
}
@Override
public void visit(Manager employee) {
System.out.println("CTO看"+employee.getName()+"的系统上线数"+employee.systemCount());
}
}
# 访问者实现二:CEO
public class CEOVisitor implements Visitor {
@Override
public void visit(Coder employee) {
System.out.println("CEO看"+employee.getName()+"的KPI"+employee.getKpi());
}
@Override
public void visit(Manager employee) {
System.out.println("CEO看"+employee.getName()+"的KPI"+employee.getKpi());
}
}
public class Test {
private static List<Employee> employeeList = new ArrayList();
static {
Coder coder = new Coder("张小开", 90);
Manager manager = new Manager("李经理", 90);
employeeList.add(coder);
employeeList.add(manager);
}
public static void main(String[] args) {
System.out.println("CTO===看员工");
CTOVisitor ctoVisitor = new CTOVisitor();
for (Employee employee : employeeList) {
employee.accept(ctoVisitor);
}
System.out.println("CEO=====看员工");
CEOVisitor ceoVisitor = new CEOVisitor();
for (Employee employee : employeeList) {
employee.accept(ceoVisitor);
}
}
}
优点:
缺点:
总结:
访问者模式是一种比较复杂的设计模式,使用场景较少,数据行为类设计模式。
是指由一个工厂根据不同的参数决定创建出那种产品。
- 需要创建的对象比较少
- 客户端只需要传入工厂类的参数,不需要关心创建对象的逻辑。
优点:只需要传入一个参数就可以获取想要的对象。
缺点:工厂类的职责过重,增加新的对象时需要修改创建工厂类的判断逻辑,违被了开闭原则
。
需求
根据不同的审批流程渠道获取申请人信息
实现代码:
public class CustomerSimpleFactory {
public static CustomerInterface getInstance(String taskType){
if("1".equals(taskType)){
return new MainCustomerService();
}else if("2".equals(taskType)){
return new EleCustomerService();
}else if("3".equals(taskType)){
return new PreCustomerService();
}else if("4".equals(taskType)){
return new QrCustomerService();
}
return null;
}
public static CustomerInterface getInstance2(Class<? extends CustomerInterface> clazz){
try {
return clazz.newInstance();
} catch (Exception e) {
}
return null;
}
}
定义一个工厂的接口,由子类实现工厂接口来决定创建的对象,工厂方法模式让类的实例推迟到子类工厂中
1.创建对象需要由大量的逻辑,(实例化目标类时逻辑比较复杂)
2.预计有较多的新实例需要加入
优点:
1.客户不需要知道类具体的创建细节 ,只需要关系返回的结果(与简单工厂一样有这个优点,似乎所有的工厂都是这个优点)
2.方便扩展新产品,符合开闭原则
缺点:
1.类的代码太多,增加了类的结构复杂度,2.增加了系统的抽象复杂度。
/**
* 工厂方法模式
*/
public interface CustomerFactory {
CustomerInterface getInstance();
}
/** 实现(一) 主流程 */
class MainCustomerFactory implements CustomerFactory{
@Override
public CustomerInterface getInstance() {
return null;
}
}
/** 实现(二) 预决策 */
class PreCustomerFactory implements CustomerFactory{
@Override
public CustomerInterface getInstance() {
return null;
}
}
class Test{
public static void main(String[] args) {
CustomerFactory factory = new MainCustomerFactory();
CustomerInterface instance = factory.getInstance();
}
}
指通过工厂创建一个具有产品,而同一个产品内有多个相互依赖或相关的接口。
比较绕口,举一个实际例子:总共有4个流程,其中每个流程都有获取申请人和担保人的方法。
优点:
1.具体产品在应用层代码隔离,无需关心具体创建细节。
2.将一系列的产品组放在一起创建。(如:主流程申请人和主流程担保人在一起创建)
缺点:
1.产品中扩展新产品困难,需要修改抽象类,如新增一个联系人则需要修改抽象工厂方法,所有的子类都需要实现。
2.类结构复杂,增加了系统的抽象性、复杂度。
我们代码实现定义中举的例子:
总共有4个流程,其中每个流程都有获取申请人和担保人的方法。
分析:
1.工厂目标:客户端需要根据自己的需要,获取4个不同的流程产品对象。
2. 每一个产品对象都分别由获取申请人信息和担保人信息的方法(申请人和担保人相关的接口)
//客户接口,以及不同流程的实现类
public interface Customer {
}
class MainCustomer implements Customer{
}
class PreCustomer implements Customer{
}
//担保人接口,以及不同流程的实现类
public interface Guarantor {
}
class MainGuarantor implements Guarantor{
}
class PreGuarantor implements Guarantor{
}
// 抽象工厂
public abstract class TaskFactory {
public void init(){
//一些初始化操作
}
public abstract Customer getCustomer();
public abstract Guarantor getGuarantor();
}
//主流程抽象工厂实现
class MainTaskFactory extends TaskFactory{
@Override
public Customer getCustomer() {
return new MainCustomer();
}
@Override
public Guarantor getGuarantor() {
return new MainGuarantor();
}
}
//预批流程工厂实现
class PreTaskFactory extends TaskFactory{
@Override
public Customer getCustomer() {
return new PreCustomer();
}
@Override
public Guarantor getGuarantor() {
return new PreGuarantor();
}
}
class Test {
public static void main(String[] args) {
TaskFactory factory = new MainTaskFactory();
Customer customer = factory.getCustomer();
Guarantor guarantor = factory.getGuarantor();
}
}
桥接模式又称双维度扩展模式,实际应用场景不太多。是将抽象部分与具体实现分离,使他们都可以独立变化。通过组合的方式将他们建立联系,而不是通过继承。
一个类存在两个或多个独立变化的维度,且这两个维度都需要进行独立扩展。
举例:发送通知的方式有邮件、短信两种方式,而通过发送通知的方式又有加急、普通。
优点:
1.分离抽象与具体
2.提高了系统的扩展性
3.复合开闭原则
缺点:
1.增加了系统的复杂度和设计难度
2.需要准确的识别系统中两个独立变化的维度(实际项目中难度较大)
上面的例子【发送通知的方式有邮件、短信两种方式,而通过发送通知的方式又有加急、普通。】实现代码
public interface Message {
void send(String message);
}
class EmailMessage implements Message{
@Override
public void send(String message) {
System.out.println("发送邮件信息:"+message);
}
}
class PhoneMessage implements Message{
@Override
public void send(String message) {
System.out.println("发送手机短信信息:"+message);
}
}
//抽象加急、普通短信桥接
public abstract class AbstractMessageBridge {
private Message message;
public AbstractMessageBridge(Message message){
this.message = message;
}
public void send(String message) {
this.message.send(message);
}
}
//普通短信
class NormalMessage extends AbstractMessageBridge{
public NormalMessage(Message message) {
super(message);
}
@Override
public void send(String message) {
super.send("[普通]"+message);
}
}
//加急短信
class UrgentMessage extends AbstractMessageBridge{
public UrgentMessage(Message message) {
super(message);
}
@Override
public void send(String message) {
super.send("[加急]"+message);
}
}
// 测试
class Test {
public static void main(String[] args) {
AbstractMessageBridge message = new UrgentMessage(new EmailMessage());
message.send("紧急休假");
AbstractMessageBridge message2 = new NormalMessage(new EmailMessage());
message2.send("休假");
AbstractMessageBridge message3 = new NormalMessage(new PhoneMessage());
message3.send("休假");
}
}
数据库驱动与操作数据的API
观察者模式又称发布订阅模式(Publish/Subscribe),定义一种一对多的依赖关系,一个主题对象可以被多个观察者同时监听,每当主题对象状态变化时,多依赖的对象都会被通知到。
- 当一个或多个对象的变化依赖另一个对象的变化
- 实现类似的广播机制,发布者无需知道具体的收听者,感兴趣的对象会自动接受该广播
public interface Subject<E> {
void addObserver(Observer<E> observer);
/**
* 通知对象
* @param event
*/
void notify(E event);
}
public class NodeCodeSubject implements Subject<String> {
private List<Observer<String>> observers = new ArrayList<>();
== 通知所有观察者
@Override
public void notify(String event) {
for (Observer<String> observer : observers) {
observer.update(event);
}
}
== 增加观察者的方法
@Override
public void addObserver(Observer<String> observer) {
observers.add(observer);
}
}
public interface Observer<E> {
/**
* 观察者对象变化
* @param event
*/
void update(E event);
}
public class NodeCodeObserver implements Observer<String>{
@Override
public void update(String event) {
System.out.println("监听到节点变化了nodeCode:"+event);
}
}
public class Test {
public static void main(String[] args) {
NodeCodeObserver nodeCodeObserver = new NodeCodeObserver();
NodeCodeObserver nodeCodeObserver2 = new NodeCodeObserver();
Subject subject = new NodeCodeSubject();
subject.addObserver(nodeCodeObserver);
subject.addObserver(nodeCodeObserver2);
subject.notify("end");
}
}
public class NodeCodeEvent {
@Subscribe
public void assignee(String nodeCode){
System.out.println("监听到nodeCode改变:"+nodeCode);
}
}
# 测试
public class Test {
public static void main(String[] args) {
EventBus eventBus = new EventBus();
eventBus.register(new NodeCodeEvent());
eventBus.post("evl");
eventBus.post("visit");
}
}
监听节点的变化来分别处理不同的逻辑同时满足一个观察者可以监听多个节点的功能,分别实现如:刷新接口,人工派件等功能。
public class NodeInfo {
private Long caseId;
private String nodeCode;
// ignore getting/setting
}
继承 InitializingBean 为了结合Spring使用,提醒子类主动注册观察者
public interface NodeObserver extends InitializingBean {
/**
* 需要监听的节点
* @return
*/
List<String> registerCode();
/**
* 节点变化
* @param nodeInfo
*/
void nodeChange(NodeInfo nodeInfo);
}
public class NodeCodeManager {
//注册的程序通过Key,Map存储到这里
private static Map<String,List<NodeObserver>> map = new HashMap<>();
public static void register(NodeObserver observer){
for (String nodeCode : observer.registerCode()) {
List<NodeObserver> list = null;
if((list = map.get(nodeCode)) == null ){
list = new ArrayList<>();
}
list.add(observer);
map.put(nodeCode,list);
}
}
/**
* 节点变化,通知对应的节点
* @param nodeInfo
*/
public static void notifyAll(NodeInfo nodeInfo){
List<NodeObserver> list = map.get(nodeInfo.getNodeCode());
if(list != null){
for (NodeObserver nodeObserver : list) {
nodeObserver.nodeChange(nodeInfo);
}
}
}
}
public class RefreshCoreService implements NodeObserver {
@Override
public List<String> registerCode() {
// 注册这两个节点作为这个类的观察节点,实现同时观察指定的多个接口
return Arrays.asList("eval","vrf2");
}
@Override
public void nodeChange(NodeInfo nodeInfo) {
System.out.println("现在的节点是:"+nodeInfo.getNodeCode());
System.out.println("开始刷新接口.....");
}
@Override
public void afterPropertiesSet() throws Exception {
//主动注册本实现作为观察者
NodeCodeManager.register(this);
}
}
public abstract class AbstractNodeCodeObserver implements NodeObserver {
//这个抽象类的作用是帮助子类注册作为观察者,子类无需在重复定义
@Override
public void afterPropertiesSet() throws Exception {
NodeCodeManager.register(this);
}
}
@Service
public class AssigneeNodeObserver extends AbstractNodeCodeObserver {
@Override
public List<String> registerCode() {
return Arrays.asList("vrf","evl","visit");
}
@Override
public void nodeChange(NodeInfo nodeInfo) {
System.out.println("案件:caseId:"+nodeInfo.getCaseId()+"开始派件:"+nodeInfo.getNodeCode());
}
}
#测试,使用spring环境
@SpringBootTest
class SpbootApplicationTests {
@Resource
private AssigneeNodeObserver assigneeNodeObserver;
@Test
public void notifyAllTest() {
NodeInfo nodeInfo = new NodeInfo();
nodeInfo.setCaseId(1L);
nodeInfo.setNodeCode("vrf");
NodeCodeManager.notifyAll(nodeInfo);
}
}
是一种按照规定语法进行解析的方案,如给定一个字符串表达式“a+b-c+…”,这种两个或以上的数字进行运算,如何优雅的实现呢?这就是解释器模式要做的事情。
目前在项目中能遇到的场景比较少,如Spring的EL表达式就实现了很多的表达式,包括运算。
实现上述例子代码较多,见源码:https://chenglj.coding.net/public/spboot/spboot/git/files/master/src/main/java/org/chenglj/spboot/design/interpreter
上面的例子结构清晰,实际业务算法比较复杂的话优先使用该例子,但似乎有点类爆炸,在调用方式完全不变的情况下可以简化成2个类,具体见代码。
采用的是函数式接口的方式,用新增方法替换新增类。
1.新增一个函数式接口,和例子1是一样的,只不过不去实现该接口而已
@FunctionalInterface
public interface ExpressionFunction {
int interpreter(Map<String,Integer> context);
}
2.实现方法
public class Calculator2 {
private ExpressionFunction expressionFunction;
public int interpreter(String key,Map<String,Integer> context) {
return context.get(key);
}
public int add(ExpressionFunction left, ExpressionFunction right, Map<String,Integer> context) {
return left.interpreter(context) + right.interpreter(context);
}
public int sub(ExpressionFunction left, ExpressionFunction right, Map<String,Integer> context) {
return left.interpreter(context) - right.interpreter(context);
}
//后续加减乘除等运算只需要添加方法即可,不需要添加类
public Calculator2(String expression) {
char[] exprArray = expression.toCharArray();
Stack<ExpressionFunction> stack = new Stack<>();
for (int i = 0; i < exprArray.length; i++) {
char expr = exprArray[i];
if('+'== expr) {
int z = ++i;
ExpressionFunction function = (x) -> add(stack.pop(),(c) -> interpreter(String.valueOf(exprArray[z]),c),x);
stack.add(function);
} else if('-' == expr) {
int z = ++i;
ExpressionFunction function = (x) -> sub(stack.pop(),(c) -> interpreter(String.valueOf(exprArray[z]),c),x);
stack.add(function);
} else {
int j = i;
stack.add((x) -> interpreter(String.valueOf(exprArray[j]),x));
}
}
this.expressionFunction = stack.pop();
}
public int getValue(Map<String,Integer> context) {
return expressionFunction.interpreter(context);
}
}
3.测试
public class Client {
public static void main(String[] args) {
String expression = "a+b-c-d";
Map<String,Integer> context = new HashMap<>();
context.put("a",11);
context.put("b",6);
context.put("c",3);
context.put("d",7);
System.out.println(new Calculator2(expression).getValue(context));
}
}
源码分享:例子2源码
在例子2的基础还还可以简化一个类,使用JDK提供原生的Function<T,R>替换ExpressionFunction,不过这种方案不推荐。
public class Calculator3 {
private Function<Map<String,Integer>,Integer> function;
public int interpreter(String key,Map<String,Integer> context) {
return context.get(key);
}
public int add(Function<Map<String,Integer>,Integer> left
,Function<Map<String,Integer>,Integer> right,Map<String,Integer> context) {
return left.apply(context) + right.apply(context);
}
public int sub(Function<Map<String,Integer>,Integer> left,
Function<Map<String,Integer>,Integer> right,
Map<String,Integer> context) {
return left.apply(context) - right.apply(context);
}
//后续加减乘除等运算只需要添加方法即可,不需要添加类
public Calculator3(String expression) {
char[] exprArray = expression.toCharArray();
Stack<Function<Map<String,Integer>,Integer>> stack = new Stack<>();
for (int i = 0; i < exprArray.length; i++) {
char expr = exprArray[i];
if('+'== expr) {
int z = ++i;
Function<Map<String,Integer>,Integer> function =
(x) -> add(stack.pop(),(c) -> interpreter(String.valueOf(exprArray[z]),c),x);
stack.add(function);
} else if('-' == expr) {
int z = ++i;
Function<Map<String,Integer>,Integer> function =
(x) -> sub(stack.pop(),(c) -> interpreter(String.valueOf(exprArray[z]),c),x);
stack.add(function);
} else {
int j = i;
stack.add((x) -> interpreter(String.valueOf(exprArray[j]),x));
}
}
this.function = stack.pop();
}
public int getValue(Map<String,Integer> context) {
return function.apply(context);
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。