这是在OOP中命名方法的简单原理,我尝试在我的代码中遵循: 操作时是动词 , 构建时是名词 。 而已。 两者之间什么都没有。 诸如saveFile()
或getTitle()
不适合使用,必须重命名和重构。 此外,“操作”的方法必须始终返回void
,例如print()
或save()
。 让我解释。
首先,我必须说这个想法与Bertrand Meyer在他的《面向对象的软件构造》一书中提出的想法非常相似,他在书中建议我们将一个对象的方法分为两个截然不同的类别:查询和命令。
该原理背后的思想是很哲学的。 让我们从构建器开始,这些构建器应该创建或找到一个对象然后返回它。 假设我有一个书店,请我给我一本书的名字:
interface Bookshelf {Book find(String title);
}
显然,它是一个“构建者”(或用迈耶的术语来说是一个“查询”)。 我要一本书,这本书已经送给我了。 但是,问题出在方法的名称上。 它被称为“查找”,这意味着我知道如何处理这本书。 将会发现。
但是,这不是我们应该如何对待对象的方式。 我们决不能告诉他们如何做我们希望他们做的工作。 取而代之的是,我们必须让他们决定是要找到,构造这本书,还是从内存缓存中取出这本书。 当我们查询时,我们必须说出我们要寻找什么结果,然后让对象来决定要如何构造该结果。 此方法更合适的名称是book()
:
interface Bookshelf {Book book(String title);
}
经验法则是:建造者始终是名词。 如果该方法返回某些内容,则必须为名词。 最好其名称应说明该方法返回的内容。 如果是一本书,则将其命名为book()
。 如果是文件,请调用方法file()
等。以下是一些好的构建器示例:
interface Foo {float speed(Actor actor);Money salary(User user);File database();Date deadline(Project project, User user);
}
相反,以下是一些名称不正确的生成器的示例:
interface Foo {float calculateSpeed(Actor actor);Money getSalary(User user);File openDatabase();Date readDeadline(Project project, User user);
}
在构建者名称中没有动词的位置!
顺便说一句,不仅是名字。 由于其名称不包含动词,因此构建器不应对封装的实体进行任何修改。 它只能创建或找到某些东西并将其返回。 就像纯函数一样 ,它一定不能有任何副作用。
接下来,有“操纵者”(或用迈耶的术语来说是“命令”)。 它们为我们做了一些工作,修改了对象封装的实体。 它们与构建者相反,因为它们实际上是对由对象抽象的世界进行更改。 例如,我们要求Bookshelf
向其添加一本新书:
interface Bookshelf {void add(Book book);
}
该方法将书籍添加到存储中。 我们不知道存储将如何精确地修改。 但是我们知道,由于方法的名称是动词,因此会进行修改。
另外,操纵器不得返回任何东西。 我们将其视为回应的类型始终是void
。 这主要是为了将代码的命令性部分与声明性部分分开。 我们要么接收对象,要么告诉他们该怎么做。 我们绝不能将这些活动混为一谈。
这些规则的目的是使代码更简单。 如果您遵循它们,并且所有构建器仅返回对象,而机械手仅修改世界,则整个设计将变得更易于理解。 方法将更小,其名称更短。
当然,通常很难找到这些名称。 您有时会想从操纵器中返回一些东西,或者让您的构建器进行一些更改,例如对缓存进行更改。 试着抵制这种诱惑并坚持原则:方法要么是构建器,要么是操纵器,中间没有东西。 上面的示例相当原始,现实生活中的代码要复杂得多。 但这就是原理将帮助我们的事情-使代码更简单。
我也知道名词/动词原则,该原则建议始终将类命名为名词,而将其方法命名为动词。 我认为这是一个错误的主意,因为它不会将制造商与操作员区分开,并鼓励我们始终根据命令性指令进行思考。 我相信,即使我们有时不得不从其他对象获取它们,而不是通过构造函数实例化它们,OOP也必须更多地涉及对象的声明性组合。 这就是为什么我们在大多数情况下确实需要构建器的原因,并且还必须看到它们与其他方法(操纵器)之间的明显区别。
翻译自: https://www.javacodegeeks.com/2018/08/builders-and-manipulators.html