首页 > 其他分享 >【适配器】设计模式:旧系统迁移与第三方库集成的解决方案

【适配器】设计模式:旧系统迁移与第三方库集成的解决方案

时间:2024-09-04 18:23:59浏览次数:13  
标签:... String 解决方案 适配器 接口 public 设计模式 void

引言

适配器设计模式是一种结构设计模式,它允许将一个类的接口转换成客户端期望的另一个接口。适配器让那些接口不兼容的类可以一起工作。这种模式在系统集成、插件开发和第三方库集成中尤为重要。

核心组件:

  • 目标接口(ITarget):期望的接口,要转化成的接口定义,客户端将使用这个接口;
  • 适配者(Adaptee):已存在的类,具有不兼容Target定义的接口;
  • 适配器(Adaptor):将适配者Adaptee的接口转换成目标ITarget的接口。

实现方式

类适配器

类适配器基于继承实现

// 目标接口
public interface ITarget {
	void f1();
	void f2();
	void fc();
}
// 适配者
public class Adaptee {
	public void fa() { //... }
	public void fb() { //... }
	public void fc() { //... }
}
//适配器
public class Adaptor extends Adaptee implements ITarget {
	public void f1() {
		super.fa();
	}

	public void f2() {
		//...重新实现f2()...
	}

	//fc()不需要实现,直接继承自Adaptee,这是跟对象适配器最大的不同点
}

对象适配器

对象适配器基于组合实现

// 目标接口
public interface ITarget {
	void f1();
	void f2();
	void fc();
}
//适配者
public class Adaptee {
	public void fa() { ... }
	public void fb() { ... }
	public void fc() { ... }
}
//适配器
public class Adaptor implements ITarget {
	private Adaptee adaptee;

	public Adaptor(Adaptee adaptee) {
		this.adaptee = adaptee;
	}

	public void f1() {
		adaptee.fa(); //委托给Adaptee
	}

	public void f2() {
		//...重新实现f2()...
	}

	public void fc() {
		adaptee.fc();
	}
}

方案选择

类适配器和对象适配器在实际工作中该怎么选择呢?有以下两个维度

  • Adaptee接口的个数
  • AdapteeITarget接口的契合程度

1)若Adaptee接口不多,则两种方式都可以;
2)若Adaptee接口很多,且AdapteeITarget接口定义大部分都相同,推荐使用类适配器,因为Adaptor复用父类Adaptee的接口,比起对象适配器的实现方式,Adaptor的代码量要少一些;
3)若Adaptee接口很多,且AdapteeITarget接口定义大部分都不相同,推荐使用对象适配器,因为组合结构相对于继承更加灵活。

应用场景

适配器模式可以看作一种“补偿模式”,用来补救设计上的缺陷,属于无奈之举。如果在设计初期,就能协调规避接口不兼容的问题,那么这种模式就没有应用的机会了。
那么在实际开发中,什么情况下会出现接口不兼容呢?

封装有缺陷的接口设计

假如我们依赖的外部系统在接口设计上有缺陷(比如包含大量静态方法),引入之后会影响到我们自身代码的可测试性。为了隔离设计上的缺陷,我们希望对外部系统提供的接口进行二次封装,抽象出更好的接口设计。

//假设这个类来自外部sdk,我们无权修改它的代码
public class CD { 
	public static void staticFunction1() { //... }

	public void uglyNamingFunction2() { //... }

	public void tooManyParamsFunction3(int paramA, int paramB, ...) { //... }

	public void lowPerformanceFunction4() { //... }
}

// 使用适配器模式进行重构
// 目标接口
public interface ITarget {
	void function1();
	void function2();
	void fucntion3(ParamsWrapperDefinition paramsWrapper);
	void function4();
	//...
}
//适配器
public class CDAdaptor extends CD implements ITarget {
	public void function1() {
		super.staticFunction1();
	}

	public void function2() {
		super.uglyNamingFucntion2();
	}

	public void function3(ParamsWrapperDefinition paramsWrapper) {
		super.tooManyParamsFunction3(paramsWrapper.getParamA(), ...);
	}

	public void function4() {
		//...reimplement it...
	}
}

统一多个类的接口设计

某个功能的实现依赖多个外部系统或者多个类,通过适配器模式,将它们的接口适配为统一的接口定义,然后就可以使用多态特性来复用代码逻辑。

假如系统 A 要对用户输入的文本进行敏感词过滤,为了提高过滤的召回率,引入了多款第三方敏感词过滤系统,依次对用户输入的内容进行过滤,过滤掉尽可能多的敏感词。
但是,每个系统提供的过滤接口都是不同的。意味着我们无法复用一套逻辑来调用各个系统。此时,我们可以使用适配器模式,将所有系统的接口适配为统一的接口定义,这样我们可以复用调用敏感词过滤的代码。

未使用适配器模式
// A敏感词过滤系统提供的接口
public class ASensitiveWordsFilter { 
	//text是原始文本,函数输出用***替换敏感词之后的文本
	public String filterSexyWords(String text) {
		// ...
	}

	public String filterPoliticalWords(String text) {
		// ...
	} 
}
// B敏感词过滤系统提供的接口
public class BSensitiveWordsFilter  { 
	public String filter(String text) {
		//...
	}
}
// C敏感词过滤系统提供的接口
public class CSensitiveWordsFilter { 
	public String filter(String text, String mask) {
		//...
	}
}

// 未使用适配器模式之前的代码:代码的可测试性、扩展性不好
public class RiskManagement {
	private ASensitiveWordsFilter aFilter = new ASensitiveWordsFilter();
	private BSensitiveWordsFilter bFilter = new BSensitiveWordsFilter();
	private CSensitiveWordsFilter cFilter = new CSensitiveWordsFilter();

	public String filterSensitiveWords(String text) {
		String maskedText = aFilter.filterSexyWords(text);
		maskedText = aFilter.filterPoliticalWords(maskedText);
		maskedText = bFilter.filter(maskedText);
		maskedText = cFilter.filter(maskedText, "***");
		return maskedText;
	}
}
适配器模式改造
// 统一接口定义
public interface ISensitiveWordsFilter { 
	String filter(String text);
}
// A 适配器
public class ASensitiveWordsFilterAdaptor implements ISensitiveWordsFilter {
	private ASensitiveWordsFilter aFilter;
	public String filter(String text) {
		String maskedText = aFilter.filterSexyWords(text);
		maskedText = aFilter.filterPoliticalWords(maskedText);
		return maskedText;
	}
}
// B 适配器
public class BSensitiveWordsFilterAdaptor implements ISensitiveWordsFilter {
	private BSensitiveWordsFilter bFilter;
	public String filter(String text) {
		String maskedText = bFilter.filter(text);
		return maskedText;
	}
}
// C 适配器
public class CSensitiveWordsFilterAdaptor implements ISensitiveWordsFilter {
	private CSensitiveWordsFilter cFilter;
	public String filter(String text) {
		String maskedText = cFilter.filter(text,"");
		return maskedText;
	}
}

// 扩展性更好,更加符合开闭原则,如果添加一个新的敏感词过滤系统,
// 这个类完全不需要改动;而且基于接口而非实现编程,代码的可测试性更好。
public class RiskManagement { 
	private List<ISensitiveWordsFilter> filters = new ArrayList<>();

	public void addSensitiveWordsFilter(ISensitiveWordsFilter filter) {
		filters.add(filter);
	}

	public String filterSensitiveWords(String text) {
		String maskedText = text;
		for (ISensitiveWordsFilter filter : filters) {
			maskedText = filter.filter(maskedText);
		}
		return maskedText;
	}
}

替换依赖的外部系统

当我们把项目中依赖的一个外部系统替换为另一个外部系统时,利用适配器模式,可以减少对代码的改动。

假设我们有一个外部系统 A 的接口和实现。

public interface IA {
    void fa();
}

public class A implements IA {
    public void fa() {
        System.out.println("Implementation of fa in System A");
    }
}

还有一个使用外部系统 A 的 Demo 类

public class Demo {
    private IA a;

    public Demo(IA a) {
        this.a = a;
    }

    public void doSomething() {
        a.fa();
    }
}

现在,我们引入外部系统B以及适配器BAdaptor

public class B {
    public void fb() {
        System.out.println("Implementation of fb in System B");
    }
}

// B的适配器
public class BAdaptor implements IA {
    private B b;

    public BAdaptor(B b) {
        this.b = b;
    }

    public void fa() {
        // 适配B的fb方法
        b.fb();
    }
}

最后,我们可以在Demo中无缝替换AB,而不影响Demo的代码

public class Client {
    public static void main(String[] args) {
        // 使用外部系统A
        Demo demoWithA = new Demo(new A());
        demoWithA.doSomething();

        // 替换为外部系统B,借助适配器
        Demo demoWithB = new Demo(new BAdaptor(new B()));
        demoWithB.doSomething();
    }
}

通过引入适配器,我们在替换外部系统时只需修改实例的创建,而不需要修改Demo中的代码。这符合适配器模式的核心思想,实现了对项目代码的最小侵入性。

适配不同格式的数据

Java 中的 Arrays.asList() 也可以看作一种数据适配器,将数组类型的数据转化为集合容器类型。

List<String> stooges = Arrays.asList("A", "B", "C");

总结

适配器设计模式是一种强大的工具,它允许不兼容的接口协同工作。通过创建适配器,我们可以将现有的类集成到新系统中,而无需修改它们的代码。这种模式提高了代码的可重用性和系统的灵活性。

参考资料:
《极客时间-设计模式之美》

标签:...,String,解决方案,适配器,接口,public,设计模式,void
From: https://blog.csdn.net/yqq962464/article/details/141858625

相关文章

  • IIS相关错误报错汇总整理及解决方案
    解决方案400BadRequest:检查请求是否包含错误的信息或格式。401Unauthorized:确认是否已经进行了身份验证。403Forbidden:检查是否有足够的权限访问资源。404NotFound:确认请求的URL是否正确,资源是否存在。500InternalServerError:检查服务器日志,寻找错误信息。503Ser......
  • 【Redis】缓存击穿、缓存穿透、缓存雪崩原理以及多种解决方案
    一、前言在SpringCloud微服务集群项目中,客户端的请求首先会经过Nginx,Nginx会将请求反向代理到Gateway网关层,接着才会将请求发送到具体的服务service。在service中如果要查询数据,则会到缓存中查询,如果缓存未命中,再到数据库中查询数据。作为缓存的Redis扛住了系统......
  • 深入探讨楼宇自控系统解决方案所带来的益处
    楼宇自控系统是指利用先进的自动化技术和智能控制手段,实现对建筑物内部各类设备、系统进行集中监测、控制、管理的系统。这一系统不仅提高了楼宇运行效率,还改善了工作环境,并为节能减排提供了重要保障。下面我们将从多个角度深入探讨楼宇自控系统解决方案所带来的益处。1.智能化监......
  • 策略性数据恢复:在MySQL中应对挑战与解决方案
    数据恢复是数据库管理中的关键环节,尤其是在面对数据丢失或损坏的情况下。MySQL作为广泛使用的数据库系统,提供了多种工具和策略来支持有效的数据恢复。本文将深入探讨如何在MySQL中实现数据恢复的策略问题解决,包括数据备份策略的选择、恢复过程的优化、以及如何通过自动化和......
  • Vue3打造前台+中台通用开发提效解决方案-42种前台业务模型
    Vue3打造前台+中台通用开发提效解决方案:‌42种前台业务模型引言随着前端技术的飞速发展,‌Vue.js作为主流前端框架之一,‌凭借其易用性和高性能,‌在开发领域占据了一席之地。‌特别是Vue3的发布,‌带来了CompositionAPI、‌Teleport等新特性,‌进一步提升了开发效率和用户体验。‌......
  • 前端Pinia教程,Pinia+Vue3+Vite+TypeScript+腾讯IM聊天解决方案项目实战
    前端Pinia教程:‌Pinia+Vue3+Vite+TypeScript+腾讯IM聊天解决方案项目实战在前端开发中,‌随着Vue3的普及和Vite构建工具的兴起,‌结合TypeScript和Pinia进行状态管理成为了一个高效且受欢迎的选择。‌本文将详细介绍如何结合这些技术栈以及腾讯IM聊天解决方案,‌搭建一个高效的前端......
  • C#设计模式入门实战教程
    思维导航什么是设计模式设计模式的作用设计模式分类创建型模式(CreationalPatterns)结构型模式(StructuralPatterns)行为型模式(BehavioralPatterns)C#实现的设计模式示例代码推荐学习书籍项目源码地址优秀项目和框架精选什么是设计模式设计模式是对面向对象设计中......
  • Uni-App 制作网易云音乐多端APP:‌安卓、‌苹果、‌小程序一站式跨平台解决方案
    Uni-App制作网易云音乐多端APP:‌安卓、‌苹果、‌小程序一站式跨平台解决方案在当前的移动应用开发领域,‌跨平台开发已经成为一种趋势,‌旨在减少开发成本、‌提高开发效率,‌并为用户提供一致的使用体验。‌Uni-App,‌作为一个基于Vue.js的跨平台前端框架,‌凭借其编写一次、‌运行......
  • 设计模式-离线并发模式-隐含锁(Implicit Lock)
    作用允许框架或层超类型代码来获取离线锁锁机制应该由应用隐含的完成,而不是由开发人员编写代码完成,这样可以避免编写锁代码的疏忽而造成的数据不一致等情况。实现机制实现隐含锁就是要分解代码,在应用程序框架中完成那些无法逾越的锁机制。在悲观离线锁的任务中,会出现两......
  • 设计模式-粗粒度锁(Coarse-Grained Lock)
    作用用一个锁所住一组相关的对象粗粒度锁是覆盖多个对象的单个锁,这样不仅简化了加锁行为本身,而且让你不必为了给它们加锁所而加载所有的对象。运行机制实现粗粒度锁的第一步是为一组对象建立一个控制点,使得只用一个锁就能锁住多个对象。用乐观离线锁让组中每个对象都共享......