【设计模式】观察者模式

文章目录

引例

能够处理多种格式的资源文件的工具

处理功能1:将把三种格式文件中的文本内容抽取出来放到 .txt 文件

方案一

代码如下:

java 复制代码
// 资源抽象类
public abstract class ResourceFile { // 抽象类
	protected String filePath;
	public ResourceFile(String filePath) {
		this.filePath = filePath;
	}
	public abstract void extract2txt();
}
// PPT文件实现类
public class PPTFile extends ResourceFile {  
// PPT文件实现类,其他实现类自行实现
	public PPTFile(String filePath) {
		super(filePath);
	}
	@Override
	public void extract2txt() {
	//...省略一大坨从PPT中抽取文本的代码...
	//...将抽取出来的文本保存在跟 filePath 同名的.txt文件中...
    System.out.println("Extract PPT.");
    }
}
// ToolApplicaiton类
public class ToolApplication {
	public static void main(String[] args) {   List<ResourceFile> resourceFiles = listAllResourceFiles();       for (ResourceFile resourceFile : resourceFiles) {        resourceFile.extract2txt();
	}
	}
	private static List<ResourceFile> listAllResourceFiles() {
		List<ResourceFile> resourceFiles = new ArrayList<>();        //...后续可根据后缀(pdf/ppt/word)由工厂方法创建不同的类对象 (PdfFile/PPTFile/WordFile)
			resourceFiles.add(new PdfFile("a.pdf"));        resourceFiles.add(new WordFile("b.doc"));      resourceFiles.add(new PPTFile("c.ppt"));
	        return resourceFiles;
	        }
	}

说明:

  1. 若工具需扩展功能,如添加压缩功能(zipFile),构建索引功能(createIndex)等,所有类的代码均需要修改,是违反OCP的
  2. 每个ResourceFile具体类都是包含该种资源文件所有的功能,违反Single responsibility原则

方案二

分离变化点,将ResourceFile中变化的部分------对文件的操作分离出去,保留不变的部分------文件信息或者说数据

说明:这里在设计过程中考虑到了Java的单分派特性,采用的是将ResourceFile资源文件类主动注入到工具类Extractor中的策略,避免出现如下所示的编译错误(由于extractor.extract2txt方法并没有接收ResourceFile类型参数的重载方法)

java 复制代码
public static void main(String[] args) {
	Extractor extractor = new Extractor();
	List<ResourceFile> resourceFiles = listAllResourceFiles();
	for (ResourceFile resourceFile : resourceFiles) {     extractor.extract2txt(resourceFile);// 编译错误!
	}
}

代码说明:

java 复制代码
// 资源抽象类
public abstract class ResourceFile {
	protected String filePath;
	public ResourceFile(String filePath) {
		this.filePath = filePath;
	}
	abstract public void extracted2txtBy(Extractor extractor);
}

// PPT具体类
public class PPTFile extends ResourceFile {
	public PPTFile(String filePath) {
		super(filePath);
	}
	@Override
	public void extracted2txtBy(Extractor extractor) {
		extractor.extract2txt(this);
	}
}
// ToolApplication工具类
public class ToolApplication {
public static void main(String[] args) {
Extractor extractor = new Extractor(); //新建一个访问资源的对象
List<ResourceFile> resourceFiles = listAllResourceFiles();      for (ResourceFile resourceFile : resourceFiles) {         resourceFile.extracted2txtBy(extractor); //资源文件主动调用这个资源的访问者
	}
}
	private static List<ResourceFile> listAllResourceFiles() {      List<ResourceFile> resourceFiles = new ArrayList<>();     //...根据后缀(pdf/ppt/word)由工厂方法创建不同的类对象(PdfFile/PPTFile/WordFile)									
resourceFiles.add(new PdfFile("a.pdf")); resourceFiles.add(new WordFile("b.word"));     resourceFiles.add(new PPTFile("c.ppt"));
	return resourceFiles;
				}
	}

// Extractor工具类
public class Extractor {
	public void extract2txt(PPTFile pptFile) {
	//...
		System.out.println("Extract PPT.");
	}
	public void extract2txt(PdfFile pdfFile) {
	//...
		System.out.println("Extract PDF.");
	}
	public void extract2txt(WordFile wordFile) {
	//...
		System.out.println("Extract WORD.");
	}
}

说明:

  1. 工具类添加新功能时,还是需要修改每个资源文件类,违反OCP

方案三

将工具类做成一个层次类,抽象出Visitor接口,针对不同的资源文件类型,使用不同的visit抽象重载方法,具体的工具类实现这些方法对资源文件进行对应的处理

代码实现:

java 复制代码
// Visitor抽象类
public interface Visitor {
	void visit(PdfFile pdfFile);
	void visit(PPTFile pptFile);
	void visit(WordFile wordFile);
}
// Visitor具体类------Compressor
public class Compressor implements Visitor {
	@Override
	public void visit(PdfFile pdfFile) {
		//...
		System.out.println("Compress PDF.");
	}
	@Override
	public void visit(PPTFile pptFile) {
		//...
		System.out.println("Compress PPT.");
	}
	@Override
	public void visit(WordFile wordFile) {
		//...
		System.out.println("Compress WORD.");
	}
}
// 资源文件抽象类
public abstract class ResourceFile {
	protected String filePath;
	public ResourceFile(String filePath) {
		this.filePath = filePath;
	}
	public abstract void accept(Visitor visitor);
}
// PPT具体类
public class PdfFile extends ResourceFile {
	public PdfFile(String filePath) {
		super(filePath);
	}
	@Override
	public void accept(Visitor visitor) {
		visitor.visit(this);
	}
}
// ToolApplication类
public class ToolApplication {
	public static void main(String[] args) {
	List<ResourceFile> resourceFiles = listAllResourceFiles();       Visitor extractor = new Extractor();
		for (ResourceFile resourceFile : resourceFiles) {
			resourceFile.accept(extractor);
		}
	   Visitor compressor = new Compressor();
		for (ResourceFile resourceFile : resourceFiles) {
			resourceFile.accept(compressor);
		}
	}
	private static List<ResourceFile> listAllResourceFiles() {
		List<ResourceFile> resourceFiles = new ArrayList<>();
		//...根据后缀(pdf/ppt/word)由工厂方法创建不同的类对象(PdfFile/PPTFile/WordFile)
		resourceFiles.add(new PdfFile("a.pdf"));
		resourceFiles.add(new WordFile("b.word"));
		resourceFiles.add(new PPTFile("c.ppt"));
		return resourceFiles;
		}
	}

方案四

将ResourceFile 的集合和对该集合的操作分离出来,形成一个ObjectStructer类。而ResourceFile中仅仅包含对单个具体某类文件的操作(visit)

ps:下图说明的是一个带有聚合结果的访问者模式图,所谓聚合结构即是ObjectStructure的引入

java 复制代码
// ObjectStructure类
public class ObjectStructure {
	private List<ResourceFile> resourceFiles = new ArrayList<>();
	public void add(ResourceFile resourceFile){ resourceFiles.add(resourceFile);
	}
	public void handle(Visitor visitor){
		for (ResourceFile resourceFile : resourceFiles) {
			resourceFile.accept(visitor);
		}
	}
}
// ToolApplication类
public class ToolApplication {
	public static void main(String[] args) {
		ObjectStructure objectStructure = new ObjectStructure();
		objectStructure.add(new PdfFile("a.pdf"));
		objectStructure.add(new WordFile("b.word"));
		objectStructure.add(new PPTFile("c.ppt"));
		objectStructure.handle(new Compressor());
		objectStructure.handle(new Extractor());
	}
}

访问者模式理论

无聚合结构的Visitor模式示例图:

带有聚合结构的Visitor示例图:

使用场景

  1. 针对一组有着相同接口/相同父类的不同类型对象,对这组对象进行一组不想关的业务操作
  2. 将对象与操作解耦,将业务操作定义在独立细分的访问者类中

行为型的观察者模式在一定程度上和结构型的桥接模式有一些类似的思想,都是分离变化点

桥接模式将导致变化的类型(比如大杯、小杯杯型的因素)分离开来了,而观察者模式将对不同种类的被观察对象施加的操作分离开来了。

桥接模式的变化因素针对不同原始对象带来的变化是相同的(以不同杯型的冰淇凌为例),但观察者模式则不然,它的操作是随着被观察对象变化而变化的,这种变化体现在功能类中有一系列用被观察对象作为参数的重载方法上。

评价

优点

  1. 扩展性好:能够在不修改对象结构中元素的情况下,为对象结构中的元素添加新功能,针对功能添加满足OCP
  2. 灵活性好:被访问的对象和访问操作分开使得操作集合灵活

缺点

  1. 针对被访问对象的增加,Visitor模式是不满足OCP的
  2. 破坏封装,被访问对象层次类中细节是需要公布给访问者的
  3. 违反依赖倒置原则,Visitor模式中工具类依赖具体类
相关推荐
鱼跃鹰飞2 小时前
设计模式系列:工厂模式
java·设计模式·系统架构
老蒋每日coding9 小时前
AI Agent 设计模式系列(十九)—— 评估和监控模式
人工智能·设计模式
会员果汁10 小时前
23.设计模式-解释器模式
设计模式·解释器模式
四谎真好看14 小时前
JavaWeb学习笔记(Day08+Day09)之Mybatis入门+基础操作
笔记·学习·学习笔记·javaweb
「QT(C++)开发工程师」17 小时前
C++设计模式
开发语言·c++·设计模式
茶本无香17 小时前
设计模式之七—装饰模式(Decorator Pattern)
java·设计模式·装饰器模式
漂洋过海的鱼儿1 天前
设计模式——EIT构型(三)
java·网络·设计模式
老蒋每日coding2 天前
AI Agent 设计模式系列(十八)—— 安全模式
人工智能·安全·设计模式
老蒋每日coding2 天前
AI Agent 设计模式系列(十六)—— 资源感知优化设计模式
人工智能·设计模式·langchain
老蒋每日coding2 天前
AI Agent 设计模式系列(十七)—— 推理设计模式
人工智能·设计模式