所谓的“边界”是指外来代码(三方程序包、开放源代码、其他团队打造的组件和子系统)和自己写的代码之间进行整合的连接区域
1.使用第三方代码
1.第三方程序包和框架提供者追求普适性,这样就能在多个环境中工作,吸引广泛的用户。
2.我们建议不要将Map(或在边界上的其他接口)在系统中传递,把它保留在类或近亲类中,避免从API中返回边界接口,或将接口作为参数传递给公共API。
比如应用程序可能构造一个map对象并传递它。我们的初衷可能是map对象的所有接收者都不要删除映射图中的任何东西。但map正好有一个clear方法。
Map<Sensor> sensors = new HashMap<Sensor>();
Sensor s = sensors.get(sensorId)
复制代码
在系统中不受限制的传递Map的实体,意味着当Map的接口被修改的时,有许多地方都要跟着改。
使用Map更整洁的方式大致如下。Sensors的用户不关心是否用了泛型,那将是实现细节才关心的
public class Sensor {
private Map sensors = new HashMap();
public Sensor getById(String id){
return (Sensor) sensors.get(id);
}
}
复制代码
再比如
简单场景:生成一个Map对象,设置一个键值对,并获取它
const p = new Map();
p.set('name', 'yxfan');
p.get('name');
// 代码二
class Person {
constructor(){
this.person = new Map();
}
set() {
this.person.set('name', 'yxfan');
}
get() {
return this.person.get('name');
}
}
const p = new Person();
p.set();
p.get('name');
复制代码
思考:代码一和代码二的区别?代码二有什么好处?
- 代码一的Map提供了太多的可操作空间
- 代码二将丰富的接口隐藏了,只保留了满足特定需求的接口,从而避免了误用
2.浏览和学习边界
在利用第三方程序包时,该从何入手呢?????
- 我们没有测试第三方代码的职责,但为要使用的第三方代码编写测试,可能最符合我们的利益。
- 不要在生产代码中实验新东西,而是编写测试来遍览和理解第三方代码。
文章中把它叫做“学习性测试”。学习性测试是一种精确试验,帮助我们增进对API的理解。当第三方程序包发布了新版本,我们可以运行学习性测试,看看程序包的行为有没有改变,确保第三方程序包按照我们想要的方式工作。
举个echart例子
const myChart = echarts.init(document.getElementById('echart-container'));
const option = {
title: {
text: 'ECharts 入门示例'
},
tooltip: {},
legend: {
data: ['销量']
},
xAxis: {
data: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"]
},
yAxis: {},
series: [{
name: '销量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20]
}]
}
myChart.setOption(option);
复制代码
写完这个测试我们就知道怎么去使用echarts了,包括怎么样去初始化和配置它,然后我们就可以按照所学到的知识把echarts封装成一个我们自己的类,这样的它的边界就变成我们想要的样子。
class Echart {
constructor(container) {
this.myChart = echarts.init(container);
}
setOption(option) {
this.myChart.setOption(option);
}
clear() {
this.myChart.clear();
}
}
const myChart = new Echart(document.getElementById('echart-container'));
myChart.setOption(option);
复制代码
- 学习(新api的过程) log4j
public class LogTest {
private Logger logger;
@Before
public void initialize() {
logger = Logger.getLogger("logger");
logger.removeAllAppenders();
Logger.getRootLogger().removeAllAppenders();
}
@Test
public void basicLogger() {
BasicConfigurator.configure();
logger.info("basicLogger");
}
@Test
public void addAppenderWithStream() {
logger.addAppender(new ConsoleAppender(
new PatternLayout("%p %t %m%n"),
ConsoleAppender.SYSTEM_OUT));
logger.info("addAppenderWithStream");
}
@Test
public void addAppenderWithoutStream() {
logger.addAppender(new ConsoleAppender(
new PatternLayout("%p %t %m%n")));
logger.info("addAppenderWithoutStream");
}
}
复制代码
写完这个测试,我们就已经知道怎么去使用log4j了,包括怎么去初始化和配置它,然后我们就可以按照学到的知识把log4j封装成一个我们自己的类,这样它的边界变成我们想要的样子。
3.学习性测试的好处不只是免费
-
学习性测试是一种精确试验,帮助我们增进对api的理解
-
当第三方程序包发布了新版本,我们可以运行学习性测验,看看程序包的行为有没有改变
-
学习性测试确保第三方程序包按照我们想要的方式工作。一旦整合进来,就不能保证第三方代码总与我们的需要兼容。如果第三方程序包的修改与测试不兼容,我们也能马上发现
4.使用尚不存在的代码
还有一种边界,那种将已知和未知分隔开的边界。在代码中总有许多地方是我们的知识未及之处。有时我们并不往边界那边看过去
编写我们想得到的接口,好处之一是它在我们控制之下。这有助于保持客户代码更可读,集中于它该完成的工作。
这里我觉得书里的例子就非常的好
5.整洁的边界
-
1.边界上的改动,有良好的软件设计,无需巨大投入和重写即可进行修改
-
2.边界上的代码需要清晰的分割和定义了期望的测试。依靠你能控制的东西,好过依靠你控制不了的东西,免得日后受它控制
-
3.可以使用ADAPTER模式将我们的接口转换为第三方提供的接口
6.适配器模式
Adapter模式也叫适配器模式,是构造型模式之一,通过Adapter模式可以改变已有类(或外部类)的接口形式。
在大规模的系统开发过程中,我们常常碰到诸如以下这些情况:我们需要实现某些功能,这些功能已有还不太成熟的一个或多个外部组件,如果我们自己重新开发这些功能会花费大量时间;所以很多情况下会选择先暂时使用外部组件,以后再考虑随时替换。但这样一来,会带来一个问题,随着对外部组件库的替换,可能需要对引用该外部组件的源代码进行大面积的修改,因此也极可能引入新的问题等等。如何最大限度的降低修改面呢?
Adapter模式就是针对这种类似需求而提出来的。Adapter模式通过定义一个新的接口(对要实现的功能加以抽象),和一个实现该接口的Adapter(适配器)类来透明地调用外部组件。
这样替换外部组件时,最多只要修改几个Adapter类就可以了,其他源代码都不会受到影响。
(继承的方式)
public class Current {
public void use220V() {
System.out.println("使用220V电波");
}
}
public class Adapter extends Current{
public void use18V() {
System.out.println("使用适配器");
this.use220V();
}
}
复制代码
(委让的方式)
public class Adapter2 {
private Current current;
public Adapter2(Current current) {
this.current = current;
}
public void use18V() {
System.out.println("使用适配器");
this.current.use220V();
}
}
复制代码
主函数
public class MainClass {
public static void main(String[] args) {
// Current current = new Current();
// current.use220V();
// Adapter adapter = new Adapter();
// adapter.use18V();
Adapter2 adapter = new Adapter2(new Current());
adapter.use18V();
}
}
复制代码
优点:
- 1、可以让任何两个没有关联的类一起运行。
- 2、提高了类的复用。
- 3、增加了类的透明度。
- 4、灵活性好。
缺点:
-
1.过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
-
2.由于 JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。
使用场景:有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式。
6.参考文献
《代码整洁之道》
fanxiaopa.top/2021/01/11/…
blog.csdn.net/ymybxx/arti…
关注公众号“程序员面试之道”
回复“面试”获取面试一整套大礼包!!!
本公众号分享自己从程序员小白到经历春招秋招斩获10几个offer的面试笔试经验,其中包括【Java】、【操作系统】、【计算机网络】、【设计模式】、【数据结构与算法】、【大厂面经】、【数据库】期待你加入!!!