包阅导读总结
1. `SOLID Principles`、`Java`、`Object-Oriented Programming`、`Code Examples`、`Software Design`
2. 本文介绍了 Java 中的 SOLID 原则,通过代码示例讲解了每个原则的含义和应用,指出遵循这些原则可使代码更高效、易读和可维护,最后总结强调了 SOLID 原则对开发可扩展和可复用应用的重要性。
3.
– SOLID 原则是面向对象编程中的五个设计原则
– 单一职责原则:每个类应只有单一、聚焦的职责。
– 开放封闭原则:组件应开放扩展,封闭修改。
– 里氏替换原则:子类应能替换父类对象不影响程序正确性。
– 接口隔离原则:应构建小而聚焦的接口,不强制客户端实现不需要的行为。
– 依赖倒置原则:高层模块不应依赖低层模块,应遵循抽象和松耦合。
– 每个原则均配有 Java 代码示例以助理解
– 最后总结 SOLID 原则是开发高可扩展性和可复用性应用的基础
思维导图:
文章地址:https://www.freecodecamp.org/news/introduction-to-solid-principles/
文章来源:freecodecamp.org
作者:Anjan Baradwaj
发布时间:2024/6/24 15:45
语言:英文
总字数:1043字
预计阅读时间:5分钟
评分:90分
标签:SOLID,Java
以下为原文内容
本内容来源于用户推荐转载,旨在分享知识与观点,如有侵权请联系删除 联系邮箱 media@ilingban.com
In this article, you’ll learn about the SOLID principles. You’ll gain an understanding of each principle along with Java code examples.
SOLID principles are a set of five design principles used in object-oriented programming. Adhering to these principles will help you develop robust software. They will make your code more efficient, readable, and maintainable.
SOLID is an acronym that stands for:
- Single Responsibility Principle
- Open/Closed Principle
- Liskov Substitution Principle
- Interface Segregation Principle
- Dependency Inversion Principle
Single Responsibility Principle
The single responsibilty principle states that every class must have a single, focused responsibility, a single reason to change.
public class Employee{ public String getDesignation(int employeeID){ public void updateSalary(int employeeID){ public void sendMail(){ }
In the above example, the Employee
class has a few employee class-specific behaviors like getDesignation
& updateSalary
.
Additionally, it also has another method named sendMail
which deviates from the responsibility of the Employee
class.
This behavior is not specific to this class, and having it violates the single responsibility principle. To overcome this, you can move the sendMail
method to a separate class.
Here’s how:
public class Employee{ public String getDesignation(int employeeID){ public void updateSalary(int employeeID){ }public class NotificationService { public void sendMail() { }
Open/Closed Principle
According to the open/closed priniciple, components must be open for extension, but, closed for modification. To understand this principle, let us take an example of a class that calculates the area of a shape.
public class AreaCalculator(){ public double area(Shape shape){ double areaOfShape; if(shape instanceof Square){ } else if(shape instanceof Circle){ } return areaOfShape; }
The problem with the above example is that if there is a new instance of type Shape
for which you need to calculate the area in the future, you have to modify the above class by adding another conditional else-if
block. You will end up doing this for every new object of the Shape
type.
To overcome this, you can create an interface and have each Shape
implement this interface. Then, each class can provide its own implementation for calculating the area. This will make your program easily extensible in the future.
interface IAreaCalculator(){ double area();}class Square implements IAreaCalculator{ @Override public double area(){ System.out.println("Calculating area for Square"); return 0.0; }}class Circle implements IAreaCalculator{ @Override public double area(){ System.out.println("Calculating area for Circle"); return 0.0; }}
Liskov Substitution Principle
The Liskov substitution principle states that you must be able to replace a superclass object with a subclass object without affecting the correctness of the program.
abstract class Bird{ abstract void fly();}class Eagle extends Bird { @Override public void fly() { }class Ostrich extends Bird { @Override public void fly() { }
In the above example, the Eagle
class and the Ostrich
class both extend the Bird
class and override the fly()
method. However, the Ostrich
class is forced to provide a dummy implementation because it cannot fly, and therefore it does not behave the same way if we replace the Bird
class object with it.
This violates the Liskov substitution principle. To address this, we can create a separate class for birds that can fly and have the Eagle
extend it, while other birds can extend a different class, which will not include any fly
behavior.
abstract class FlyingBird{ abstract void fly();}abstract class NonFlyingBird{ abstract void doSomething();}class Eagle extends FlyingBird { @Override public void fly() { }class Ostrich extends NonFlyingBird { @Override public void doSomething() { }
Interface Segregation Principle
According to the interface segregation principle, you should build small, focused interfaces that do not force the client to implement behavior they do not need.
A straightforward example would be to have an interface that calculates both the area and volume of a shape.
interface IShapeAreaCalculator(){ double calculateArea(); double calculateVolume();}class Square implements IShapeAreaCalculator{ double calculateArea(){ double calculateVolume(){ }
The issue with this is that if a Square
shape implements this, then it is forced to implement the calculateVolume()
method, which it does not need.
On the other hand, a Cube
can implement both. To overcome this, we can segregate the interface and have two separate interfaces: one for calculating the area and another for calculating the volume. This will allow individual shapes to decide what to implement.
interface IAreaCalculator { double calculateArea();}interface IVolumeCalculator { double calculateVolume();}class Square implements IAreaCalculator { @Override public double calculateArea() { }class Cube implements IAreaCalculator, IVolumeCalculator { @Override public double calculateArea() { @Override public double calculateVolume() {}
Dependency Inversion Principle
In the dependency inversion principle, high-level modules should not depend on low-level modules. In other words, you must follow abstraction and ensure loose coupling
public interface Notification { void notify();}public class EmailNotification implements Notification { public void notify() { System.out.println("Sending notification via email"); }}public class Employee { private EmailNotification emailNotification; public Employee(EmailNotification emailNotification) { this.emailNotification = emailNotification; } public void notifyUser() { emailNotification.notify(); }}
In the given example, the Employee
class depends directly on the EmailNotification
class, which is a low-level module. This violates the dependency inversion principle.
public interface Notification{ public void notify();}public class Employee{ private Notification notification; public Employee(Notification notification){ this.notification = notification; } public void notifyUser(){ notification.notify(); } } public class EmailNotification implements Notification{ public void notify(){ } } public static void main(String [] args){ Notification notification = new EmailNotification(); Employee employee = new Employee(notification); employee.notifyUser(); }
In the above example, we have ensured loose coupling. Employee
is not dependent on any concrete implementation, rather, it depends only on the abstraction (notification interface).
If we need to change the notification mode, we can create a new implementation and pass it to the Employee
.
Conclusion
In conclusion, we’ve covered the essence of SOLID principles through straightforward examples in this article.
These principles form the building blocks for developing applications that are highly extensible and reusable.
Let’s connect on LinkedIn