《代码整洁之道》阅读笔记 8整洁的边界

1,233

所谓的“边界”是指外来代码(三方程序包、开放源代码、其他团队打造的组件和子系统)和自己写的代码之间进行整合的连接区域 在这里插入图片描述

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】、【操作系统】、【计算机网络】、【设计模式】、【数据结构与算法】、【大厂面经】、【数据库】期待你加入!!!

1.计算机网络----三次握手四次挥手

2.梦想成真-----项目自我介绍

3.你们要的设计模式来了

4.震惊!来看《这份程序员面试手册》!!!

5.一字一句教你面试“个人简介”

6.接近30场面试分享