• Java8实战-总结34


    重构、测试和调试

    使用 Lambda 重构面向对象的设计模式

    观察者模式

    观察者模式是一种比较常见的方案,某些事件发生时(比如状态转变),如果一个对象(通常称之为主题)需要自动地通知其他多个对象(称为观察者),就会采用该方案。创建图形用户界面GUI)程序时,经常会使用该设计模式。这种情况下,在图形用户界面组件(比如按钮)上注册一系列的观察者。如果点击按钮,观察者就会收到通知,并随即执行某个特定的行为。 但是观察者模式并不局限于图形用户界面。比如,观察者设计模式也适用于股票交易的情形,多个券商可能都希望对某一支股票价格(主题)的变动做出响应。下图通过UML图解释了观察者模式。
    在这里插入图片描述
    Twitter这样的应用设计并实现一个定制化的通知系统。想法很简单:好几家报纸机构,比如《纽约时报》《卫报》以及《世界报》都订阅了新闻,他们希望当接收的新闻中包含他们感兴趣的关键字时,能得到特别通知。首先,需要一个观察者接口,它将不同的观察者聚合在一起。它仅有一个名为notify的方法,一旦接收到一条新的新闻,该方法就会被调用:

    interface Observer { 
    	void notify(String tweet); 
    } 
    
    • 1
    • 2
    • 3

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

    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); 
    		} 
    	} 
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    最重要的部分是Subject,为它定义一个接口:

    interface Subject { 
    	void registerObserver(Observer o); 
    	void notifyObservers(String tweet); 
    } 
    
    • 1
    • 2
    • 3
    • 4

    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)); 
    	} 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

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

    	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!"); 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    上面示例中,《卫报》会特别关注这条新闻。
    使用Lambda表达式
    Observer接口的所有实现类都提供了一个方法:notify。新闻到达时,它们都只是对同一段代码封装执行。Lambda表达式的设计初衷就是要消除这样的僵化代码。使用Lambda表达式后,无需显式地实例化三个观察者对象,直接传递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); 
    	} 
    }); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    是否随时随地都可以使用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); 
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    下图以UML的方式阐释了责任链模式。
    在这里插入图片描述
    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"); 
    	} 
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

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

    	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); //打印输出“From Raoul, Mario and Alan: Aren't lambdas really sexy?!!
    
    • 1
    • 2
    • 3
    • 4
    • 5

    使用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?!!");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  • 相关阅读:
    SocketTool V4.0 使用说明
    消息队列实现分布式事务
    Ubuntu20.4安装QT6
    Few-Shot Learning
    Nautilus Chain上线主网,为DeFi和流支付的未来构建基础
    [NLP] LLM---<训练中文LLama2(三)>对LLama2进行中文预料预训练
    初学Vue3(个人学习)
    nginx配置指南
    WPF——给button添加背景图片
    wx:for-item wx:for-index wx:for-key
  • 原文地址:https://blog.csdn.net/weixin_42583701/article/details/133255812