探索Java8——使用Lambda重构Java设计模式

策略模式

Java设计模式——策略模式

策略模式包含三部分内容:

  • 策略模式接口。
  • 策略模式接口的具体实现。
  • 用户(test)。

在这里插入图片描述
我们假设你希望验证输入的内容是否根据标准进行了恰当的格式化(比如只包含小写字母或数字)。你可以从定义一个验证文本(以String的形式表示)的接口入手:
策略模式的接口:

public interface ValidationStrategy { 
 	boolean execute(String s); 
} 

接口的具体实现:

public class IsAllLowerCase implements ValidationStrategy { 
 	public boolean execute(String s){ 
 		return s.matches("[a-z]+"); 
 } 
} 
public class IsNumeric implements ValidationStrategy { 
 	public boolean execute(String s){ 
 		return s.matches("\\d+"); 
 } 
} 

之后,你就可以在你的程序中使用这些略有差异的验证策略了:

public class Validator{ 
 	private final ValidationStrategy strategy; 
 	public Validator(ValidationStrategy v){ 
 		this.strategy = v; 
 	} 
 	public boolean validate(String s){ 
 		return strategy.execute(s); 
 	} 
} 
psvm:
Validator numericValidator = new Validator(new IsNumeric()); 
boolean b1 = numericValidator.validate("aaaa"); 
Validator lowerCaseValidator = new Validator(new IsAllLowerCase ()); 
boolean b2 = lowerCaseValidator.validate("bbbb"); 

而使用Lambda表达式,则是这样的:

@FunctionalInterface
public interface ValidationStrategy {
    boolean execute(String s);
}
public class Validator{
    private final ValidationStrategy strategy;
    public Validator(ValidationStrategy v){
        this.strategy = v;
    }
    public boolean validate(String s){
        return strategy.execute(s);
    }
}
psvm:
Validator numericValidator = new Validator((String s) -> s.matches("[a-z]+"));
        boolean b1 = numericValidator.validate("aaaa");
        Validator lowerCaseValidator = new Validator((String s) -> s.matches("\\d+"));
        boolean b2 = lowerCaseValidator.validate("bbbb");

正如你看到的,Lambda表达式避免了采用策略设计模式时僵化的模板代码。

模版方法模式

Java设计模式——模板方法模式

从一个例子着手,看看这个模式是如何工作的。假设你需要编写一个简单的在线银行应用。

abstract class OnlineBanking { 
 	public void processCustomer(int id){ 
		Customer c = Database.getCustomerWithId(id); 
 		makeCustomerHappy(c); 
 	} 
  	abstract void makeCustomerHappy(Customer c); 
} 

processCustomer方法搭建了在线银行算法的框架:获取客户提供的ID,然后提供服务让用户满意。不同的支行可以通过继承OnlineBanking类,对该方法提供差异化的实现。

使用Lambda表达式:
们向processCustomer方法引入了第二个参数,它是一个Consumer<Customer>类型的参数,与前文定义的makeCustomerHappy的特征保持一致。

public void processCustomer(int id, 	Consumer<Customer> makeCustomerHappy){ 
 	Customer c = Database.getCustomerWithId(id); 
 	makeCustomerHappy.accept(c); 
} 

现在,你可以很方便地通过传递Lambda表达式,直接插入不同的行为,不再需要继承OnlineBanking类了:

new OnlineBankingLambda().processCustomer(1337, (Customer c) -> 
 	System.out.println("Hello " + c.getName()); 

这是又一个例子,佐证了Lamba表达式能帮助你解决设计模式与生俱来的设计僵化问题。

观察者模式

Java设计模式——观察者模式

在这里插入图片描述
首先,你需要一个观察者接口,它将不同的观察者聚合在一起。它仅有一个名为notify的方法,一旦接收到一条新的新闻,该方法就会被调用:

interface Observer { 
 	void notify(String tweet); 
} 

声明不同的观察者(比如,这里是三家不同的报纸机构),依据新闻中不同的关键字分别定义不同的行为:

class NYTimes implements Observer{ 
	public void notify(String tweet) { 
 		if(tweet != null && tweet.contains("money")){ 
 			System.out.println("Breaking news in NY! " + tweet); 
 		} 
 	} 
} 
class Guardian implements Observer{ 
 	public void notify(String tweet) { 
 		if(tweet != null && tweet.contains("queen")){ 
 			System.out.println("Yet another news in London... " + tweet); 
 		} 
 	} 
} 
class LeMonde implements Observer{ 
 	public void notify(String tweet) { 
 		if(tweet != null && tweet.contains("wine")){ 
 			System.out.println("Today cheese, wine and news! " + tweet); 
		 } 
 	} 
} 

最重要的部分:Subject!让我们为它定义一个接口:

interface Subject{ 
 	void registerObserver(Observer o); 
 	void notifyObservers(String tweet); 
} 

Subject使用registerObserver方法可以注册一个新的观察者,使用notifyObservers方法通知它的观察者一个新闻的到来。让我们更进一步,实现Feed类:

class Feed implements Subject{ 
 	private final List<Observer> observers = new ArrayList<>(); 
 	public void registerObserver(Observer o) { 
 		this.observers.add(o); 
 	} 
 	public void notifyObservers(String tweet) { 
 		observers.forEach(o -> o.notify(tweet)); 
 	} 
} 

这是一个非常直观的实现:Feed类在内部维护了一个观察者列表,一条新闻到达时,它就进行通知。

psvm:
Feed f = new Feed(); 
f.registerObserver(new NYTimes()); 
f.registerObserver(new Guardian()); 
f.registerObserver(new LeMonde()); 
f.notifyObservers("The queen said her favourite book is Java 8 in Action!"); 

使用Lambda表达式:

f.registerObserver((String tweet) -> { 
 	if(tweet != null && tweet.contains("money")){ 
 		System.out.println("Breaking news in NY! " + tweet); 
 	} 
}); 
f.registerObserver((String tweet) -> { 
 	if(tweet != null && tweet.contains("queen")){ 
 		System.out.println("Yet another news in London... " + tweet); 
 	} 
}); 

那么,是否我们随时随地都可以使用Lambda表达式呢?答案是否定的!我们前文介绍的例子中,Lambda适配得很好,那是因为需要执行的动作都很简单,因此才能很方便地消除僵化代码。但是,观察者的逻辑有可能十分复杂,它们可能还持有状态,抑或定义了多个方法,诸如此类。在这些情形下,你还是应该继续使用类的方式。

责任链模式

Java设计模式——责任链模式

public abstract class ProcessingObject<T> { 
 	 protected ProcessingObject<T> successor; 
	 public void setSuccessor(ProcessingObject<T> successor){ 
 		this.successor = successor; 
 } 
 	public T handle(T input){ 
 		T r = handleWork(input); 
 		if(successor != null){ 
 			return successor.handle(r); 
		 } 
 		return r; 
	 } 
 	abstract protected T handleWork(T input); 
} 

handle方法提供了如何进行工作处理的框架。不同的处理对象可以通过继承ProcessingObject类,提供handleWork方法来进行创建。

public class HeaderTextProcessing extends ProcessingObject<String> { 
 	public String handleWork(String text){ 
 		return "From Raoul, Mario and Alan: " + text; 
 	} 
} 
public class SpellCheckerProcessing extends ProcessingObject<String> { 
 	public String handleWork(String text){ 
 		return text.replaceAll("labda", "lambda"); 
 	} 
} 

现在你就可以将这两个处理对象结合起来,构造一个操作序列!

ProcessingObject<String> p1 = new HeaderTextProcessing(); 
ProcessingObject<String> p2 = new SpellCheckerProcessing(); 
p1.setSuccessor(p2);
String result = p1.handle("Aren't labdas really sexy?!!"); 
System.out.println(result); 

使用Lambda表达式
为了链接这些函数,你需要使用andThen方法对其进行构造。

UnaryOperator<String> headerProcessing = 
 	(String text) -> "From Raoul, Mario and Alan: " + text;
UnaryOperator<String> spellCheckerProcessing = 
 	(String text) -> text.replaceAll("labda", "lambda"); 
Function<String, String> pipeline = 
 	headerProcessing.andThen(spellCheckerProcessing); 
String result = pipeline.apply("Aren't labdas really sexy?!!");

工厂模式

Java设计模式——工厂模式

通常,你会创建一个工厂类,它包含一个负责实现不同对象的方法,如下所示:

public class ProductFactory { 
 	public static Product createProduct(String name){ 
 		switch(name){ 
 			case "loan": return new Loan(); 
 			case "stock": return new Stock(); 
 			case "bond": return new Bond(); 
 			default: throw new RuntimeException("No such product " + name); 
 		} 
	 } 
} 

psvm:
Product p = ProductFactory.createProduct("loan"); 

使用Lambda表达式
在以前的内容中,我们已经知道可以像引用方法一样引用构造函数。比如,下面就是一个引用贷款(Loan)构造函数的示例:

Supplier<Product> loanSupplier = Loan::new; 
Loan loan = loanSupplier.get(); 

通过这种方式,你可以重构之前的代码,创建一个Map,将产品名映射到对应的构造函数:

final static Map<String, Supplier<Product>> map = new HashMap<>(); 
static { 
 	map.put("loan", Loan::new); 
 	map.put("stock", Stock::new); 
 	map.put("bond", Bond::new); 
} 

现在,你可以像之前使用工厂设计模式那样,利用这个Map来实例化不同的产品。

public static Product createProduct(String name){ 
 	Supplier<Product> p = map.get(name); 
 	if(p != null) return p.get(); 
 	throw new IllegalArgumentException("No such product " + name); 
} 

这是个全新的尝试,它使用Java 8中的新特性达到了传统工厂模式同样的效果。但是,如果工厂方法createProduct需要接收多个传递给产品构造方法的参数,这种方式的扩展性不是很好。你不得不提供不同的函数接口,无法采用之前统一使用一个简单接口的方式

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
/* * 原始需求背景: * 网宿CDN要按月收取客户的服务费用,根据流量的大小、 * 服务的类型等,收取不同的费用,收费规则如下: * web应用:1000元/M * 流媒体应用:1000元/M*0.7 * 下载应用:1000元/M*0.5 * 月末打印报表时,要罗列每个用户每个频道的费用、客户总费用, * 还要打印该客户的重要性指数,重要性指数=网页流/100+下载流量/600; * * 需求变更场景: * 系统已经开发出来了,接下来,运维部门现在希望对系统做一点修改, * 首先,他们希望能够输出xml,这样可以被其它系统读取和处理,但是, * 这段代码根本不可能在输出xml的代码中复用report()的任何行为,唯一 * 可以做的就是重写一个xmlReport(),大量重复report()中的行为,当然, * 现在这个修改还不费劲,拷贝一份report()直接修改就是了。 * 不久,成本中心又要求修改计费规则,于是我们必须同时修改xmlReport() * 和report(),并确保其一致性,当后续还要修改的时候,复制-黏贴的问题就 * 浮现出来了,这造成了潜在的威胁。 * 再后来,客服部门希望修改服务类型和用户重要性指数的计算规则, * 但还没决定怎么改,他们设想了几种方案,这些方案会影响用户的计费规则, * 程序必须再次同时修改xmlReport()和report(),随着各种规则变得越来越复杂, * 适当的修改点越 来越难找,不犯错误的机会越来越少。 * 现在,我们运用所学的OO原则和方法开始进行改写吧。 */

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值