JavaRush /Java 博客 /Random-ZH /编写代码的规则:从创建系统到使用对象

编写代码的规则:从创建系统到使用对象

已在 Random-ZH 群组中发布
大家下午好:今天我想和大家聊聊如何正确编写代码。当我刚开始编程时,没有明确写到任何地方都可以这样写,如果你这样写,我会找到你并且……。于是我脑子里就产生了很多疑问:如何正确地写,程序的这个或那个部分应该遵循什么原则等等。 编写代码的规则:从创建系统到使用对象 - 1好吧,并不是每个人都想立即深入研究《清洁代码》这样的书,因为其中写了很多内容,但一开始却很少有明确的内容。当你读完之后,你可能会打消所有编码的欲望。因此,基于上述内容,今天我想为您提供一个编写更高级别代码的小指南(一组小建议)。在本文中,我们将回顾与创建系统以及使用接口、类和对象相关的基本规则和概念。阅读本材料不会花费您太多时间,而且我希望不会让您感到无聊。我将从上到下,即从应用程序的总体结构到更具体的细节。 编写代码的规则:从创建系统到使用对象 - 2

系统

该系统的一般理想特征是:
  • 最小的复杂性- 应避免过于复杂的项目。最主要的是简单和清晰(最好=简单);
  • 易于维护- 创建应用程序时,您必须记住它将需要支持(即使不是您),因此代码应该清晰明了;
  • 弱耦合是程序不同部分之间的连接数量最少(最大限度地利用OOP原则);
  • 可重用性——设计一个能够在其他应用程序中重用其片段的系统;
  • 可移植性——系统必须能够轻松适应另一个环境;
  • 单一风格——在不同的片段中以单一风格设计一个系统;
  • 可扩展性(scalability) ——改进系统而不扰乱其基本结构(如果添加或更改一个片段,这不应该影响其余部分)。
构建一个不需要修改、不添加功能的应用程序几乎是不可能的。我们需要不断地引入新的元素,以便我们的创意能够跟上时代的步伐。这就是可扩展性发挥作用的地方。 可扩展性本质上是扩展应用程序、添加新功能、使用更多资源(或者换句话说,使用更多负载)。也就是说,我们必须遵守一些规则,比如通过增加模块化来降低系统的耦合度,这样更容易添加新的逻辑。

系统设计阶段

  1. 软件系统- 设计通用形式的应用程序。
  2. 分离为子系统/包- 定义逻辑上可分离的部分并定义它们之间的交互规则。
  3. 将子系统划分为类- 将系统的各个部分划分为特定的类和接口,并定义它们之间的交互。
  4. 将类划分为方法是根据该类的任务,对类的必要方法进行完整的定义。方法设计 - 各个方法功能的详细定义。
通常,普通开发人员负责设计,应用程序架构师负责上述项目。

系统设计的主要原则和概念

延迟初始化习惯 应用程序在使用对象之前不会花时间创建对象,这加快了初始化过程并减少了垃圾收集器负载。但你不应该做得太过分,因为这可能会导致违反模块化。将所有设计步骤转移到特定部分(例如 main )或像工厂一样工作的类可能是值得的。好的代码的特点之一是没有频繁重复的样板代码。通常,此类代码放置在单独的类中,以便可以在正确的时间调用它。 AOP 另外,我想提一下面向方面的编程。这是通过引入端到端逻辑进行编程,即将重复的代码放入类(方面)中,并在达到某些条件时调用。例如,访问某个名称的方法或访问某个类型的变量时。有时方面可能会令人困惑,因为不能立即清楚从哪里调用代码,但尽管如此,这是一个非常有用的功能。特别是在缓存或日志记录时:我们添加此功能而不向常规类添加额外的逻辑。您可以在此处阅读有关 OAP 的更多信息。 Kent Beck 提出的设计简单架构的 4 条规则
  1. 表现力——需要明确表达类的目的,这是通过正确的命名、小尺寸和遵守单一责任原则来实现的(我们将在下面更详细地讨论它)。
  2. 最少的类和方法- 如果您希望将类分解为尽可能小且单向的,您可能会走得太远(反模式 - 霰弹枪法)。这一原则要求保持系统紧凑,不要走得太远,为每个喷嚏创建一个类。
  3. 缺乏重复- 令人困惑的额外代码是糟糕的系统设计的标志,并被移动到一个单独的地方。
  4. 所有测试的执行- 通过所有测试的系统是受控的,因为任何更改都可能导致测试失败,这可以向我们表明方法内部逻辑的更改也会导致预期行为的更改。
SOLID 在设计系统时,值得考虑 SOLID 的众所周知的原则: S - single Responsible - 单一责任原则; O——开闭——开放/封闭原则; L - 里氏替换- 芭芭拉·里氏替换原则; I-interface segregation——接口分离的原则; D——dependency inversion——依赖倒置原理;我们不会具体讨论每个原则(这有点超出了本文的范围,但您可以在此处了解更多信息)

界面

也许创建一个适当的类的最重要的阶段之一是创建一个适当的接口,它将代表一个隐藏类的实现细节的良好抽象,同时将代表一组彼此明显一致的方法。让我们仔细看看 SOLID 原则之一 -接口隔离:客户端(类)不应该实现他们不会使用的不必要的方法。也就是说,如果我们正在谈论使用最少数量的方法构建接口,这些方法旨在执行该接口的唯一任务(对我来说,这与单一职责非常相似),那么最好创建几个较小的方法一个而不是一个臃肿的界面。幸运的是,一个类可以实现多个接口,就像继承的情况一样。您还需要记住接口的正确命名:名称应尽可能准确地反映其任务。当然,它越短,引起的混乱就越少。通常在接口级别编写文档注释,这反过来又帮助我们详细描述该方法应该做什么、它需要什么参数以及它将返回什么。

班级

编写代码的规则:从创建系统到使用对象 - 3让我们看看类的内部组织。或者更确切地说,是构造类时应该遵循的一些观点和规则。通常,一个类应以变量列表开头,并按特定顺序排列:
  1. 公共静态常量;
  2. 私有静态常量;
  3. 私有实例变量。
接下来是各种构造函数,按参数从少到多的顺序排列。接下来是从更开放访问到最封闭访问的方法:通常,隐藏我们想要限制的某些功能的实现的私有方法位于最底部。

班级规模

现在我想谈谈班级规模。 编写代码的规则:从创建系统到使用对象 - 4让我们记住 SOLID 的原则之一——单一职责单一责任——单一责任原则。它规定每个对象只有一个目标(职责),其所有方法的逻辑都是为了确保这一目标。也就是说,基于此,我们应该避免大型、臃肿的类(这本质上是一种反模式——“神圣对象”),如果一个类中有很多不同的、异构逻辑的方法,我们需要思考关于将其分成几个逻辑部分(类)。反过来,这将提高代码的可读性,因为如果我们知道给定类的大致用途,我们就不需要太多时间来理解方法的用途。您还需要留意类名:它应该反映它包含的逻辑。比方说,如果我们有一个类,其名称有 20 多个单词,我们需要考虑重构。每个有自尊的类都不应该有如此多的内部变量。事实上,每个方法都与其中一个或多个方法一起使用,这会导致类内更大的耦合(这正是它应该的样子,因为类应该作为一个整体)。结果,增加班级的连贯性会导致班级的连贯性减少,当然,我们的班级数量也会增加。对于一些人来说,这很烦人;他们需要更多地去上课,看看特定的大型任务是如何运作的。除此之外,每个类都是一个小模块,应该尽可能地与其他模块连接。这种隔离减少了向类添加额外逻辑时需要进行的更改数量。

对象

编写代码的规则:从创建系统到使用对象 - 5

封装

这里我们首先讲一下OOP的原则之一——封装。因此,隐藏实现并不意味着在变量之间创建一个方法层(不加考虑地限制通过单个方法、getter 和 setter 的访问,这不好,因为整个封装点都丢失了)。隐藏访问的目的是形成抽象,也就是说,类提供了我们处理数据的通用具体方法。但用户不需要确切地知道我们如何处理这些数据——它可以工作,这很好。

德墨忒耳定律

您还可以考虑德米特法则:它是一小组规则,有助于管理类和方法级别的复杂性。因此,假设我们有一个对象Car并且它有一个方法 - move(Object arg1, Object arg2)。根据迪米特定律,该方法仅限于调用:
  • 对象本身的方法Car(换句话说,就是 this);
  • 在 中创建的对象的方法move
  • 作为参数传递的对象的方法 - arg1, arg2;
  • 内部对象的方法Car(同this)。
换句话说,德墨忒尔法则有点像儿童法则——你可以和朋友交谈,但不能和陌生人交谈

数据结构

数据结构是相关元素的集合。当将对象视为数据结构时,它是由方法处理的一组数据元素,隐含地暗示其存在。也就是说,它是一个对象,其目的是存储和操作(处理)存储的数据。与常规对象的主要区别在于,对象是一组对隐含存在的数据元素进行操作的方法。你明白吗?在常规对象中,主要方面是方法,内部变量旨在其正确操作,但在数据结构中则相反:方法支持并帮助处理存储元素,这是这里的主要元素。一种数据结构是数据传输对象(DTO)。这是一个具有公共变量的类,没有方法(或只有读/写方法),可以在使用数据库、解析来自套接字的消息等时传递数据。通常,此类对象中的数据不会长期存储,而是会被保存。几乎立即转换为我们的应用程序所使用的实体。反过来,实体也是一种数据结构,但其目的是参与应用程序不同级别的业务逻辑,而 DTO 是将数据传输到应用程序或从应用程序传输数据。示例 DTO:
@Setter
@Getter
@NoArgsConstructor
public class UserDto {
    private long id;
    private String firstName;
    private String lastName;
    private String email;
    private String password;
}
一切似乎都很清楚,但在这里我们了解了混合体的存在。 混合对象是包含处理重要逻辑、存储内部元素和访问方法(获取/设置)的方法的对象。此类对象很混乱并且很难添加新方法。您不应该使用它们,因为尚不清楚它们的用途 - 存储元素或执行某种逻辑。您可以在此处了解可能的对象类型。

创建变量的原则

编写代码的规则:从创建系统到使用对象 - 6让我们思考一下变量,或者更确切地说,思考一下创建变量的原则是什么:
  1. 理想情况下,您应该在使用变量之前立即声明并初始化它(而不是创建它并忘记它)。
  2. 只要有可能,将变量声明为final,以防止其值在初始化后发生更改。
  3. 不要忘记计数器变量(通常我们在某种循环中使用它们for,也就是说,我们不能忘记重置它们,否则它会破坏我们的整个逻辑)。
  4. 您应该尝试在构造函数中初始化变量。
  5. 如果要选择使用带引用还是不带引用的对象(new SomeObject()),请选择不带引用( ),因为该对象一旦使用,就会在下次垃圾回收时被删除,不会浪费资源。
  6. 使变量的生命周期尽可能短(变量的创建和最后一次访问之间的距离)。
  7. 在循环之前立即初始化循环中使用的变量,而不是在包含循环的方法的开头初始化。
  8. 始终从最有限的范围开始,仅在必要时扩展它(您应该尝试使变量尽可能本地化)。
  9. 每个变量仅用于一个目的。
  10. 避免具有隐藏含义的变量(该变量在两个任务之间左右为难,这意味着它的类型不适合解决其中之一)。
编写代码的规则:从创建系统到使用对象 - 7

方法

编写代码的规则:从创建系统到使用对象 - 8让我们直接转向逻辑的实现,即方法。
  1. 第一条规则是紧凑性。理想情况下,一个方法不应超过 20 行,因此,如果公共方法显着“膨胀”,则需要考虑将分离的逻辑移至私有方法中。

  2. 第二条规则是命令ifelse命令while等中的块不应高度嵌套:这会显着降低代码的可读性。理想情况下,嵌套不应超过两个块{}

    还建议使这些块中的代码紧凑且简单。

  3. 第三条规则是一个方法必须只执行一个操作。也就是说,如果一个方法执行复杂、多样的逻辑,我们将其划分为子方法。因此,该方法本身将是一个外观,其目的是按正确的顺序调用所有其他操作。

    但是,如果操作看起来太简单而无法创建单独的方法怎么办?是的,有时这看起来就像用大炮射麻雀,但小方法可以带来很多好处:

    • 更容易阅读代码;
    • 在开发过程中,方法往往会变得更加复杂,如果方法最初很简单,那么使其功能复杂化会更容易一些;
    • 隐藏实施细节;
    • 促进代码重用;
    • 更高的代码可靠性。
  4. 向下的规则是,代码应该从上到下阅读:越低,逻辑深度越深,反之,越高,方法越抽象。例如,开关命令非常不紧凑且不受欢迎,但如果您不能不使用开关,则应尝试将其尽可能移到最低级别的方法中。

  5. 方法参数- 有多少是理想的?理想情况下,根本没有))但这真的会发生吗?但是,您应该尝试尽可能少地使用它们,因为数量越少,该方法就越容易使用,也越容易测试。如果有疑问,请尝试猜测使用具有大量输入参数的方法的所有场景。

  6. 另外,我想强调以布尔标志作为输入参数的方法,因为这自然意味着该方法实现多个操作(如果为 true,则执行一个操作,如果为 false,则执行另一个操作)。正如我上面所写,这不好,应该尽可能避免。

  7. 如果一个方法有大量传入参数(极值是7,但在2-3之后你应该考虑一下),你需要将一些参数分组到一个单独的对象中。

  8. 如果有多个相似的方法(重载),则必须以相同的顺序传递相似的参数:这会增加可读性和可用性。

  9. 当你向方法传递参数时,你必须确保它们都会被使用,否则参数有什么用呢?把它从界面上剪下来就可以了。

  10. try/catch它本质上看起来不太好,所以一个好的举措是将其移动到一个中间的单独方法(处理异常的方法)中:

    public void exceptionHandling(SomeObject obj) {
        try {
            someMethod(obj);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
我在上面谈到了重复代码,但我将在这里添加它:如果我们有几个重复部分代码的方法,我们需要将其移动到一个单独的方法中,这将增加方法和班级。并且不要忘记正确的名称。我将在文章的下一部分告诉您类、接口、方法和变量的正确命名的详细信息。这就是我今天的全部内容。 编写代码的规则:从创建系统到使用对象 - 9代码规则:正确命名、好评论和坏评论的力量
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION