企业级代码
有一种通用的代码编写方式,也许在Java中特别常见,但在C#中也是如此-鼓励开发人员编写尽可能多的类。 在大型企业中,这种构建代码的方式是地方性的。 解释一下:企业代码库中的每个问题都可以通过添加另一个类(除了太多的类)来解决。
为什么会发生这种编写代码的方式? 这是对太多复杂性的反应吗? 编写一个可以很容易地单独测试的小类是有一定逻辑的。 但是,当每个人都采用这种方法时,您将获得数以百万计的课程。 试图弄清楚如何分解复杂的系统是很困难的,但是如果我们不这样做,而只是继续增加更多的类,我们会使问题变得更糟而不是更好。
但是,它比这更深入。 许多人(直到最近才包括我本人在内)认为编写精心编写,可维护的代码的最佳方法是编写许多小型类。 毕竟,简单的类很容易解释,并且更有可能承担单个责任 。 但是,您如何向新开发人员解释由一百个类组成的系统呢? 我敢打赌,您最终会在白板上乱涂乱画,到处都是盒子和线条。 我确信您的设计简单而优雅,但是当我不得不参加大约100堂课时,只是想知道风景是什么样的-我需要花一些时间来理解它。 将其扩展到具有成千上万个类的企业,它会变得非常复杂:您的一般开发人员可能永远不会理解它。
一个例子
也许一个例子会有所帮助? 想象一下我正在开发一些交易中间件。 我们收到表示需要存储的交易的消息,并将消息传递到更远的系统。 现在,我们通过CSV Feed接收这些交易。 我首先创建一个TradeMessage类。
贸易讯息
private long id;
private Date timestamp;
private BigDecimal amount;
private TradeType type;
private Asset asset;
我是一个很好的小函数开发人员,因此此类是不可变的。 现在,我有两个选择:i)我编写了一个带有一百个参数的大型构造函数,或者ii)我创建了一个构建器以使本练习更加合理。 我选择选项ii)。
TradeMessageBuilder
public TradeMessageBuilder onDate(Date timestamp)
public TradeMessageBuilder forAmount(BigDecimal amount)
public TradeMessageBuilder ofType(TradeType type)
public TradeMessageBuilder inAsset(Asset asset)
public TradeMessage build()
现在,我有了一个生成器,可以从中创建TradeMessage类。 但是,构建器要求将字符串解析为日期,小数等。我还需要担心查找资产,因为TradeMessage使用Asset类,但是传入消息仅具有资产名称。
现在,我们像优秀的GOOS小开发人员一样从外进行测试。 我们从CSVTradeMessageParser(我忽略了网络或其他任何解析器)开始。
我们需要解析一行CSV,将其拆分成组成我们的TradeMessage的组成部分。 现在,我们首先需要做一些事情:
- 解析时间戳
- 解析金额
- 解析交易类型
- 在数据库中查找资产
现在,在最极端的企业疯狂中,我们可以为每个“职责”编写一个类。 但是,在这种情况下,这简直是荒谬的(尽管添加了错误处理或对代码重用的一些尝试,您会惊讶于所有这些额外的类很快看起来像是一个好主意)。
相反,我们真正真正关心的唯一问题是资产查找。 我可以添加到解析器类本身中的日期,数量和类型解析–这都是解析消息的唯一责任,因此这很有意义。
CSVTradeMessageParser
public TradeMessage parse(String csvline)
private Date parseTimestamp(String timestamp)
private BigDecimal parseAmount(String amount)
private TradeType parseType(String amount)
现在-一个测试该课程的问题测试-我如何测试所有这些私有方法? 我可以使它们的程序包可见并将我的测试放在相同的程序包中,但这很讨厌。 或者,我不得不从公共方法进行测试,模拟构建器,并验证将正确解析的值传递给构建器。 这是不理想的,因为我无法单独测试每个解析方法。 突然将它们分成单独的类似乎是一个更好的主意……
最后,我需要创建一个AssetRepository:
资产储存库
public Asset lookupByName(String name)
解析器使用它,并将检索到的资产传递给TradeMessageBuilder。
我们完成了! 简单,不是吗? 因此,如果我已使用用于模拟依赖项的接口进行测试驱动,那么我必须编写多少个类?
- 贸易讯息
- 贸易类型
- TradeMessageBuilder
- ITradeMessageBuilder
- CSVTradeMessageParser
- 资产
- 资产储存库
- IAssetRepository
- TradeMessageBuilderTest
- CSVTradeMessageParserTest
- AssetRepositoryTest
哦,由于这只是单元测试,所以我可能需要一些端到端测试来检查整个射击比赛是否可以协同工作:
- CSVTradeMessageParserIntegrationTest
12节课! 嗯,企业y。 这只是一个玩具例子。 在现实世界中,我们将拥有FactoryFactories和BuilderVisitors来真正增加混乱。
另一种方式
还有另一种方法吗? 好吧,让我们考虑一下TradeMessage是我希望人类使用的API。 关于此API的重要事项是什么?
贸易讯息
public Date getTimestamp()
public BigDecimal getAmount()
public TradeType getType()
public Asset getAsset()
public void parse(String csvline)
呼叫者真正关心的就是所有这些-从CSV获取值和进行解析。 这足以让我在测试和生产代码中使用。 在这里,我们创建了一个很好的,干净的,简单的API,它很容易解释。 无需白板,框和线以及冗长的解释。
但是我们的parse()方法呢? 这不是太复杂了吗? 毕竟,它必须分解字符串,解析日期,金额和交易类型。 一种方法有很多责任。 但是实际上看起来有多糟? 这是我的全部:
public void parse(String csvline) throws ParseException
{
String[] parts = csvline.split(',');
setTimestamp(fmt.parse(parts[0]));
setTradeType(TradeType.valueOf(parts[1]));
setAmount(new BigDecimal(parts[2]));
setAsset(Asset.withName(parts[3]));
}
当然,现在,当您增加了一些现实世界的复杂性和更好的错误处理时,可能会更像20行。
但是,让我问您,您希望拥有哪一个:12个小班或4个小班? 将复杂的操作涂在几十个类上,还是很好地用一种方法围起来,是否更好?
参考: Actively Lazy博客上来自JCG合作伙伴 David Green的企业级代码 。
翻译自: https://www.javacodegeeks.com/2012/06/enterprise-class-java-code.html