linux :/# 模式_反模式的模式:设计/测试

linux :/# 模式

自从我作为软件开发人员开始我的职业生涯已经有一段时间了。 多年来,我为许多公司工作,从事许多不同的项目,尽我最大的努力来交付它们,并对工作感到自豪。 但是,从一个公司到另一个公司,从一个项目到另一个项目,您发现一遍又一遍的糟糕的设计决策,无论代码库有多旧或有多少人接触过。 它们如此广泛地存在,以致于真正成为样式本身,从而描述了本博客的标题:反样式的样式。

我绝对不是第一个识别出它们的人,当然也不是最后一个(因此,如果存在,请让所有功劳归于原始资源)。 它们全部来自真实项目,主要是Java项目,尽管其他细节应该保持公开。 接着就,随即 …

代码优先,没有时间思考

这听起来几乎不像反模式的名称,但我相信它是。 我们都是开发人员,毫不奇怪地喜欢编码。 每当我们有了一些要实现的功能或想法时,我们通常都只是开始对其进行编码,而没有过多地考虑设计,甚至根本不在乎我们是否可以借用/重用已经存在的某些功能。 因此,一直在重新发明轮子,希望至少能享受编码部分的乐趣……

我绝对相信,花一些时间思考高级设计,从接口,特性或协议(无论您喜欢的语言是什么)方面进行学习都是必须做的。 确实并不需要花费更多的时间,但是结果却是有意义的:最后是精美且经过深思熟虑的解决方案。

TDD是一组出色的实践,可以帮助并指导您完成整个过程。 在开发测试用例时,您将一遍又一遍地进行迭代,进行更改,并且每一步都接近正确的实现。 请在您参与的每个项目中采用它。

偶尔做

好的,足够的哲学,现在我们正在转向真正的反模式。 毫无疑问,你们中许多人目睹了以下代码段:

public void setSomething(Object something) {
    if (!heavensAreFalling) {
        this.something = something;
    }
}

本质上,您调用该方法来设置一些值,并且一定希望一旦调用完成就可以设置该值。 但是,由于方法实现中存在某些逻辑,因此它实际上可能无所事事,无声无息……

这是一个非常糟糕的设计的例子。 取而代之的是,可以使用例如状态模式来建模此类交互,或者至少仅抛出一个异常,说明由于内部约束该操作无法完成,因此可以对这种交互进行建模。 是的,可能需要编写更多代码,但至少可以满足期望。

宇宙

通常,应用程序中通常有一个类引用该应用程序的每个其他类(或被其他类引用)。 例如,项目中的所有类都必须从某个基类继承。

public class MyService extends MyBase {
  ...
}
public class MyDao extends MyBase {
  ...
}

在大多数情况下,这不是一个好主意。 它不仅对单个类产生了硬性依赖关系,而且还对该家伙内部使用的每个依赖关系都产生了依赖性。 您可能会争辩说,在Java中,每个类都隐式地继承自Object 。 确实,但这是语言规范的一部分,与应用程序逻辑无关,无需模仿。

另一个极端是要有一个类充当应用程序的核心。 它知道每个服务/ dao /…,并提供访问器(大多数情况下是静态的),因此任何人都可以转向它并索要任何东西,例如:

public class Services {
    public static MyService1 getMyService1() {...}
    public static MyService2 getMyService2() {...}
    public static MyService3 getMyService3() {...}
}

这些都是宇宙,最终它们泄漏到应用程序的每个类中(很容易调用静态方法,对吗?)并将所有内容耦合在一起。 在或多或少的大型代码库中,摆脱这样的世界非常困难。

为防止此类事情毒害您的项目,请使用由Spring FrameworkDaggerGuiceCDIHK2实现的依赖项注入模式,或通过构造函数或方法参数传递必要的依赖项。 实际上,它可以帮助您及早发现气味并在它们成为问题之前解决它们。

洋葱继承

这是一个非常有趣和令人恐惧的过程,经常出现在公开和实现REST(ful)Web服务的项目中。 让我们假设您有一个Customer类,它具有许多不同的属性,例如:

public class Customer {
    private String id;
    private String firstName;
    private String lastName;
    private Address address;
    private Company company;
    ...
}

现在,已要求您创建一个API(或通过其他方式公开)以找到客户,但仅返回其idfirstNamelastName 。 听起来很简单,不是吗? 让我们去做:

public class CustomerWithIdAndNames {
    private String id;
    private String firstName;
    private String lastName;
}

看起来很整洁吧? 每个人都很高兴,但是第二天您将需要开发另一个功能,在该功能上,您需要设计一个API,该API返回idfirstNamelastNamecompany 。 听起来很容易,我们已经有了CustomerWithIdAndNames ,只需要用company丰富它即可。 继承的完美工作,对吗?

public class CustomerWithIdAndNamesAndCompany extends CustomerWithIdAndNames {
    private Company company;
}

有用! 但是一周之后,又出现了另一个请求,在这个请求中,新的API也需要公开address属性,无论如何,您就明白了。 因此,最后您将获得数十个相互扩展的类,并在其中各处添加属性(例如洋葱,因此是反模式名称)以实现API合同。

通往地狱的另一条路……这里有很多选择,最简单的是JSON Views (这是使用Jackson的一些示例 ),其中基础类保持不变,但是可以返回其不同的视图。 如果您真的很在意不获取不需要的数据,那么另一个选择是GraphQL ,这是我们上次介绍的 。 从本质上讲,这里的信息是:不要创建这样的洋葱化层次结构,使用单一表示,而是使用不同的技术来组装它并有效地获取必要的部分。

IDD:if驱动的开发

内部的大多数实际项目都是使用相当复杂的一组应用程序和业务规则构建的,并通过应用不同的可扩展性和自定义层(如果需要)来巩固。 在大多数情况下,实现选择非常简单,并且逻辑由条件语句驱动,例如:

int price = ...;
if (calculateTaxes) {
    price += taxes;
}

随着时间的流逝,业务规则不断发展,被模仿的条件表达式也不断发展,最终成为真正的怪物:

int price = ...;
if (calculateTaxes && (country == "US" || country == "GB") && 
    discount == null || (discount != null && discount.getType() == TAXES)) {
    price += taxes;
}

您会看到我要做什么,该项目建立在IDD最佳实践的基础上:if驱动的开发。 不仅代码变得脆弱,容易出现条件错误,很难遵循,而且更改也很可怕! 公平地说,您还需要测试if分支的所有可能组合,以确保它们有意义并选择了正确的分支(我敢打赌,实际上没有人这样做,因为这是大量的测试和努力)!

在许多情况下, 功能切换可能是一个答案。 但是,如果您习惯于编写这样的代码,请花一些时间阅读有关设计模式,测试驱动的开发和编码实践的书籍。 它们太多了,下面是我绝对会推荐的列表:

这里有很多很棒的东西要添加,但是这是一个很好的起点,而且投资回报丰厚。 if语句将使您更加满意。

测试彩票

当您听到诸如“我们组织中的所有项目都敏捷并且使用TDD实践”之类的信息时,就会想到理想主义的图画,您希望本书能正确完成所有工作。 实际上,在大多数项目中,事情远非如此。 希望至少有一些您可以信任的测试套件,或者可以吗?

请欢迎测试彩票:片状测试的王国。 您是否见过带有随机测试失败的构建? 就像在每个连续的构建中(未引入任何更改)一样,某些测试突然通过,但其他测试开始失败? 可能是十个构建中的一个可能变成绿色(累积奖金!),因为星星终于正确对齐了? 如果没有人在乎,这将成为一种新常态,并且每个加入该团队的团队成员都被告知要忽略这些失败,“他们已经失败了很长一段时间,把它们弄糟了”。

测试与您投入生产的主流代码一样重要。 它们需要维护,重构和保持清洁。 始终保持所有构建为绿色,如果发现有些脆弱或随机故障,请立即解决。 这并不容易,但是在许多情况下,您实际上可能会遇到真正的错误! 您必须信任您的测试套件,否则为什么根本需要它们?

测试框架工厂

这个家伙是杰出的。 它经常发生,感觉每个组织都有义务创建自己的测试框架,通常是在现有框架之上。 起初,这听起来像是个好主意,可以说确实是个好主意。 不幸的是,在99%的结果中,又是一个没人愿意使用的可怕框架,但被迫这样做是因为“开发花费了15个人/年,之后我们不能将其丢弃”。 每个人都在挣扎,生产率正以光速下降,质量根本没有提高。

走这条路之前请三思。 简化组织正在处理的复杂应用程序测试的目标无疑是一个不错的目标。 但是不要陷入束手无策的陷阱,他们将花费数月甚至数年时间孤立地研究“完善而强大的测试框架”,总体上可能会做得很好,但并不能解决任何实际问题。 相反,只是创建新的。 如果您仍然决定参加本次培训,请努力保持专注,解决其核心问题,原型,始终寻求反馈,倾听和迭代……并保持其稳定,易用,有用,可信赖且轻巧尽可能。

结论

您不能一开始就构建所有内容。 有时,我们的决定会受到压力和截止日期或我们无法控制的因素的影响。 但这并不意味着我们应该让事情永远保持这种状态,请每天变得越来越糟,请不要……

这篇文章融合了许多人的想法,这些人都是很棒的前队友和很棒的朋友,值得赞扬。 如果您碰巧喜欢它,则可能有兴趣查看即将发布的文章,我们将在该文章中讨论体系结构反模式。

翻译自: https://www.javacodegeeks.com/2017/06/patterns-antipatterns-design-testing.html

linux :/# 模式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值