面向对象设计原则
Programming is fun until you have to incorporate a new requirement that changes the whole design of your code. Everyone writes code but it’s too difficult to write a code that your colleagues can understand. It’s difficult to write a code that sucks less and doesn’t succumb to changes. However, there are a few design principles that can be learned, practiced, and absorbed so that it will make us suck less.
编程很有趣,除非您必须合并一个新要求以更改代码的整个设计。 每个人都编写代码,但是编写同事可以理解的代码太困难了。 编写难以吸收且不屈服于更改的代码是很困难的。 但是,有一些设计原则可以学习,实践和吸收,因此可以减少我们的负担。
Design principles are basic guidelines that help us to avoid bad object-oriented design. Nobody is going to point a gun towards you and make you adhere to it strictly. It’s not a rule of thumb but it will definitely help you. We are now going to see three principles.
设计原则是帮助我们避免不良的面向对象设计的基本准则。 没有人会朝您开枪并使您严格遵守。 这不是经验法则,但肯定会为您提供帮助。 现在,我们将看到三个原则。
- Encapsulate what varies. 封装各种内容。
- Favor composition over inheritance. 优先考虑组成而不是继承。
- Program to interface. 程序接口。
封装各种内容。 (Encapsulate what varies.)
The principle is so simple. Identify the aspects of the code that varies and isolate it with what stays the same. You gotta ask yourself
原理是如此简单。 确定代码方面的变化,并将其保持不变。 你要问自己
“每次获得新要求时,此代码都会更改吗?” (“Does this code change every time I get the new requirement?”)
If the answer is yes then you now know what to do. Encapsulate. Let’s see an example.
如果答案是肯定的,那么您现在知道该怎么办。 封装。 让我们来看一个例子。
public void connectToDatabase(String connectionType) {
DBConnection connection = null;
if ("mysql".equals(connectionType)) {
connection = new MySqlConnection();
} else if("mongodb".equals(connectionType)){
connection = new MongoConnection();
}else if("postgres".equals(connectionType)){
connection = new PostgresConnection();
}
connection.createConnection();
}
I bet every one of us has seen code like this and who knows we may still have such code running in production. So, let’s say we need to add an Oracle database in our code. How are we going to add it? Another else if condition, isn’t it? But let’s say there are a bunch of other important codes in the same file and while adding a new Oracle database you mistakenly touched other production codes in the same file which introduced many newer bugs. Had it been encapsulated, then there would have been a single place to change. By encapsulating it, our design will be much more flexible.
我敢打赌,我们每个人都已经看到过这样的代码,并且谁知道我们可能仍在生产环境中运行这样的代码。 因此,假设我们需要在代码中添加一个Oracle数据库。 我们如何添加它? 还有其他条件吗? 但是,假设同一文件中还有许多其他重要代码,并且在添加新的Oracle数据库时,您错误地触摸了同一文件中的其他生产代码,从而引入了许多新的错误。 如果将其封装起来,那么将只有一个地方可以更改。 通过封装,我们的设计将更加灵活。
The part that will change with every new requirement is if-else conditions. So, we can create a factory class that will be solely responsible for creating database connections. I will not show how to create a factory class but I will show how the code looks once we encapsulated all the logics inside the factory class.
如果有其他新要求,将会改变的部分是if-else条件。 因此,我们可以创建一个工厂类,该类将全权负责创建数据库连接。 我不会显示如何创建工厂类,但是一旦将所有逻辑封装在工厂类中,我将显示代码的外观。
public void connectToDatabase(String connectionType) {
DBConnectionFactory dbConnectionFactory = new DBConnectionFactory();
DBConnection connection = dbConnectionFactory.getConnection(connectionType);
connection.createConnection();
}
With this approach, let’s say if you want to add an H2 database connection then you have to change only in DBConnectionFactory class.
假设使用这种方法,如果要添加H2数据库连接,则只需在DBConnectionFactory类中进行更改。
偏爱继承而不是继承 (Favor composition over inheritance)
In object-oriented programming (OOP), inheritance is IS-A relationship (A car is a vehicle) whereas composition is HAS-A relationship (A car has an engine).
在面向对象编程(OOP)中,继承是IS-A关系(汽车是汽车),而组成是HAS-A关系(汽车有引擎)。
Inheritance provides a great way to reuse the code but it becomes less effective once the hierarchy grows on. Classes and objects created through inheritance are tightly coupled because if something is changed in the base class then it will affect its subclasses. But it’s not true for composition. The classes and objects created through composition are loosely coupled. There is another great benefit of using Composition over Inheritance. Inheritance is not easily testable because to test derived class you also need superclass but the composition is as we can use mock objects.
继承提供了一种重用代码的好方法,但是一旦层次结构发展起来,它就会变得无效。 通过继承创建的类和对象紧密耦合,因为如果在基类中进行了某些更改,则会影响其子类。 但这对构图不是正确的。 通过合成创建的类和对象是松散耦合的 。 使用“合成”而不是“继承”还有另一个好处。 继承不容易测试,因为要测试派生类,您还需要超类,但是其组成与我们可以使用模拟对象一样。
程序接口 (Program to interface)
The term interface here doesn’t merely mean the keyword we have in Java programming language. It is a kind of contract that has a bunch of rules that needs to be followed by concrete classes that implement it. It is often called as program to the supertype. If we program to an interface rather than the concrete implementations then it provides the flexibility to exploit polymorphism.
这里的接口一词不仅意味着我们在Java编程语言中拥有的关键字。 它是一种合同,它具有一堆规则,需要遵循一些规则来实现它。 它通常被称为超类型的程序。 如果我们对接口进行编程而不是对具体的实现进行编程,那么它将为利用多态性提供灵活性。
The interface provides rules and doesn’t care whoever implements it and how the implementation looks like. Let’s take a real-world simple example.
该接口提供了规则,无论在何处实施规则,以及实施的外观如何,都不在乎。 让我们以一个真实的简单示例为例。
interface FileUpload {
void upload(Object o);
}
We want to upload the file in our application. There may be many different ways to do so. We can use Cloudinary, AWS S3 bucket, upload care, etc. So the implementations of FileUpload would look like AmazonS3FileUpload, CloudinaryFileUpload, UploadCareFileUpload etcetera.
我们想将文件上传到我们的应用程序中。 可能有许多不同的方法。 我们可以使用Cloudinary,AWS S3存储桶,上传服务等。因此FileUpload的实现看起来像AmazonS3FileUpload,CloudinaryFileUpload,UploadCareFileUpload等。
Let’s program to interface.
让我们编程进行接口。
FileUpload fileUpload = new CloudinaryFileUpload();
// code related to fileupload
Great, our file uploading feature is working correctly and after one year, we want to switch our file uploading from Cloudinary to AmazonS3. We just need to change the implementation like:
太好了,我们的文件上传功能正常运行,并且在一年之后,我们希望将文件上传从Cloudinary切换到AmazonS3。 我们只需要更改实现,例如:
FileUpload fileUpload = new AmazonS3FileUpload();
It will not break other codes because we are relying on the interface and the methods provided by it. We can only use methods defined within the FileUpload interface. Had it been concrete implementation with a bunch of extra methods, then it would have created a havoc.
它不会破坏其他代码,因为我们依赖于它提供的接口和方法。 我们只能使用FileUpload接口中定义的方法。 如果它是使用一堆额外方法的具体实现,那么它将造成严重破坏。
//program to concrete implementation
CloudinaryFileUpload fileUpload = new CloudinaryFileUpload();
//code related to fileUpload
So, it’s always a good idea to program to an interface. Besides, the flexibility it provides, it is also easy to test. We can easily mock FileUpload interface wherever necessary.
因此,对接口进行编程始终是一个好主意。 此外,它提供的灵活性也很容易测试。 我们可以在必要时轻松地模拟FileUpload接口。
Thanks a lot for reading my article. If you have any suggestions, then they are welcomed.
非常感谢您阅读我的文章。 如果您有任何建议,欢迎您。
翻译自: https://medium.com/@surajgautam077/object-oriented-design-principles-ef4be2f51826
面向对象设计原则