策略模式
策略模式包含三部分内容:
- 策略模式接口。
- 策略模式接口的具体实现。
- 用户(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表达式避免了采用策略设计模式时僵化的模板代码。
模版方法模式
从一个例子着手,看看这个模式是如何工作的。假设你需要编写一个简单的在线银行应用。
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表达式能帮助你解决设计模式与生俱来的设计僵化问题。
观察者模式
首先,你需要一个观察者接口,它将不同的观察者聚合在一起。它仅有一个名为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适配得很好,那是因为需要执行的动作都很简单,因此才能很方便地消除僵化代码。但是,观察者的逻辑有可能十分复杂,它们可能还持有状态,抑或定义了多个方法,诸如此类。在这些情形下,你还是应该继续使用类的方式。
责任链模式
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?!!");
工厂模式
通常,你会创建一个工厂类,它包含一个负责实现不同对象的方法,如下所示:
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需要接收多个传递给产品构造方法的参数,这种方式的扩展性不是很好。你不得不提供不同的函数接口,无法采用之前统一使用一个简单接口的方式