代码整洁之道读书笔记——第十章:类

本文探讨了类的设计原则,包括从变量列表开始的组织方式,强调封装和内聚的重要性。提倡保持类的短小,每个类应有单一职责,以提高代码的可维护性和可扩展性。通过重构实例展示了如何遵循单一职责原则,避免修改引起的风险,并介绍了如何通过接口和抽象类来隔离修改,遵循开闭原则和依赖倒置原则。文章还提到了在设计中应考虑抽象而非细节,以降低耦合度,提高系统的灵活性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第十章 类

10.1 类的组织

从上往下,从一组变量列表开始:公共静态常量、私有静态变量,私有实体变量

封装

我们喜欢保持变量和工具函数的私有性,有时需要用到受保护变量或工具函数,好让测试可以访问到

我们首先会想办法使之保有隐私,放松封装总是下策

10.2 类应该短小

70个方法的类确实有些太长了,5个方法的类虽然方法数少,但是拥有太多的权责

类的名称应当描述其权责

10.2.1 单一权责原则

类或者模块应该有且只有一条加以改变的理由

鉴别权责(修改的理由)常常帮助我们再代码中认识到并创建出更好的抽象

而这个创建出的类极有可能在其他地方回得到复用

系统应该由许多短小的类而不是少量巨大的类组成,每个小类封装一个权责,只有一个修改的原因,并与少数其他类一起协同达成期望的系统行为

10.2.2 内聚

方法操作的变量越多,就越黏聚到类上。如果一个类中的每个变量都被每个方法所使用的,则该类具有最大的内聚性

创建极大化内聚类是既不可取也不可能的

我们希望内聚性保持在较高的位置,内聚性高,意味着类中的方法和变量互相依赖,互相结合成一个逻辑整体

保持函数和参数列表短小的策略,可能会导致一组方法所用的实体变量数量增加。出现这种情况往往以为着至少有一个类要从大类中挣扎出来,我们应该尝试将这些变量和方法拆分到更多的类中,让新的类更为内聚

10.2.3 保持内聚性就会得到许多短小的类

当我们看到一个有许多变量的大函数,我们想把该函数中某一部分拆解成单独的函数,不过,要拆出来的代码使用了函数中声明的4个变量,那是否要把这4个变量都当做参数传递到新函数中去呢?肯定是没必要的!只需要将4个变量提升为类的实体变量,就无需传递任何参数了。但是这也意味着类丧失了内聚性,因为堆积了越来越多只为允许少量函数共享而存在的实体变量。如果有些函数想要共享某些变量,可以让这几个函数拥有一个自己的类!换句话说,就是创建一个类去包含这几个函数。

作者举了一个例子:输出素数

原本全部的代码都写在了PrintPrimes的main函数中,然后经过重构优化,拆分出了PrimePrinter、RowColumnPagePrinter、PrimeGenerator三个类

10.3 为了修改而组织

作者举了一个例子:

public class Sql {
    public Sql(String table, Column[] columns)
    public String create()
    public String insert(Object[] fields)
    public String selectAll()
    private String selectWithCriteria(String criteria)
    private String valuesList(Object[] fields, final Column[] columns)
}

当新增一种语句类型时,就要修改Sql类。改动单个语句类型时,也要进行修改。说明Sql违背了单一职责原则。并且还出现了与类一小部分有关的私有方法,比如selectWithCriteria()

改进后:

abstract public class Sql {
    public Sql(String table, Column[] columns)
    abstract public String generate();
}

public class CreateSql extends Sql {
    public CreateSql(String table, Column[] columns)
    @Override
    public String generate()
}

public class InsertSql extends Sql {
    public InsertSql(String table, Column[] columns)
    @Override
    public String generate()
    private String valuesList(Object[] fields, final Column[] columns)
}

public class SelectSql extends Sql {
    public SelectSql(String table, Column[] columns)
    @Override
    public String generate()
}

......

如此一来,每个类中的代码都记为简单,函数对于其它函数造成毁坏的风险也变得几乎近无

类与类之间相互隔离了,当需要增加语句时,现存的类无需修改,只需要继承Sql类即可

重构后的Sql遵守单一职责原则,同时也遵守开闭原则

隔离修改

具体类包含细节,抽象类只呈现概念。依赖于具体的细节,细节改变时,就会有风险,我们可以借助接口和抽象类来隔离这些细节带来的影响

现在我们实现股票计算相关的代码,构建一个Portfolio类来代表投资组合的价值,如果直接依赖于外部TokyoStockExchanged的API,则测试用例就会受到价值查询连带的影响

与其设计直接依赖于TokyoStockExchange的Portfolio类,不如直接创建StockExchange接口,让TokyoStockExchange来实现这个接口:

public interface StockChange {
    Money currentPrice(String symbol);
}

public Portfolio {
    private StockExchange exchange;
    public Portfolio(StockExchange exchange) {
        this.exchange = exchange;
    }
}

通过降低连接度,我们就遵循了另一条设计原则,依赖倒置原则,类应该依赖于抽象而不是依赖于细节

总结

这节其实更多的说了一些设计模式相关的东西,所说的所讲的代码都在阐述那几个原则,有时候代码需要多看,就慢慢的会明白其中的含义了。接口设计能力是很重要的一项能力,我们要明白什么时候该去考虑抽象,什么时候该去考虑细节。

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值