SOLID 原则概念
SOLID 原则是面向对象编程中使用的五项设计原则。遵循这些原则将帮助您开发出强大的软件。它们将使您的代码更高效、更易读、更易于维护。理解和应用这些原则可以有效地提高代码质量,并降低软件开发成本。
SOLID 是以下单词的首字母缩写:
- 单一职责原则
- 开放/封闭原则
- 里氏替换原则
- 接口隔离原则
- 依赖倒置原则
单一职责原则
单一责任原则规定每个类必须具有单一的、集中的责任,以及单一的改变原因。
public class Employee{
public String getDesignation(int employeeID){ // }
public void updateSalary(int employeeID){ // }
public void sendMail(){ // }
}
在上面的例子中,Employee
该类具有一些员工类特定的行为,如getDesignation
& updateSalary
。
此外,它还有另一个sendMail
偏离Employee
类的职责的方法。
此行为并非特定于此类,并且违反了单一责任原则。为了克服这个问题,您可以将该sendMail
方法移至单独的类。
就是这样:
public class Employee{
public String getDesignation(int employeeID){ // }
public void updateSalary(int employeeID){ // }
}
public class NotificationService {
public void sendMail() { // }
}
开放/封闭原则
根据开放/封闭原则,组件必须对扩展开放,但对修改封闭。为了理解这一原则,让我们以计算形状面积的类为例。
public class AreaCalculator(){
public double area(Shape shape){
double areaOfShape;
if(shape instanceof Square){
// calculate the area of Square
} else if(shape instanceof Circle){
// calculate the area of Circle
}
return areaOfShape;
}
上述示例的问题是,如果Shape
将来有一个新的类型实例需要计算其面积,则必须通过添加另一个条件else-if
块来修改上述类。最终,您将对该Shape
类型的每个新对象执行此操作。
为了解决这个问题,您可以创建一个接口并让每个类都Shape
实现该接口。然后,每个类都可以提供自己的面积计算实现。这将使您的程序在将来易于扩展。
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;
}
}
里氏替换原则
里氏替换原则指出,您必须能够用子类对象替换超类对象,而不会影响程序的正确性。
abstract class Bird{
abstract void fly();
}
class Eagle extends Bird {
@Override
public void fly() { // some implementation }
}
class Ostrich extends Bird {
@Override
public void fly() { // dummy implementation }
}
在上面的例子中,Eagle
类和Ostrich
类都扩展了Bird
类并重写了fly()
方法。但是,Ostrich
类被迫提供虚拟实现,因为它不能飞,因此如果我们用Bird
它替换类对象,它的行为方式将不一样。
这违反了里氏替换原则。为了解决这个问题,我们可以为会飞的鸟创建一个单独的类并对其进行Eagle
扩展,而其他鸟可以扩展一个不同的类,该类不包含任何fly
行为。
abstract class FlyingBird{
abstract void fly();
}
abstract class NonFlyingBird{
abstract void doSomething();
}
class Eagle extends FlyingBird {
@Override
public void fly() { // some implementation }
}
class Ostrich extends NonFlyingBird {
@Override
public void doSomething() { // some implementation }
}
接口隔离原则
根据接口隔离原则,您应该构建小型、集中的接口,不要强迫客户端实现他们不需要的行为。
一个简单的例子是有一个可以计算形状的面积和体积的界面。
interface IShapeAreaCalculator(){
double calculateArea();
double calculateVolume();
}
class Square implements IShapeAreaCalculator{
double calculateArea(){ // calculate the area }
double calculateVolume(){ // dummy implementation }
}
问题在于,如果一个Square
形状实现了这一点,那么它就会被迫实现calculateVolume()
它不需要的方法。
另一方面,aCube
可以同时实现两者。为了解决这个问题,我们可以分离接口并拥有两个单独的接口:一个用于计算面积,另一个用于计算体积。这将允许各个形状决定要实现什么。
interface IAreaCalculator {
double calculateArea();
}
interface IVolumeCalculator {
double calculateVolume();
}
class Square implements IAreaCalculator {
@Override
public double calculateArea() { // calculate the area }
}
class Cube implements IAreaCalculator, IVolumeCalculator {
@Override
public double calculateArea() { // calculate the area }
@Override
public double calculateVolume() {// calculate the volume }
}
依赖倒置原则
在依赖倒置原则中,高级模块不应该依赖于低级模块。换句话说,你必须遵循抽象并确保松散耦合
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();
}
}
在给定的示例中,Employee
类直接依赖于EmailNotification
类,而类是低级模块。这违反了依赖倒置原则。
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(){
//implement notification via email
}
}
public static void main(String [] args){
Notification notification = new EmailNotification();
Employee employee = new Employee(notification);
employee.notifyUser();
}
在上面的例子中,我们确保了松散耦合。Employee
不依赖于任何具体的实现,而是仅依赖于抽象(通知接口)。
如果我们需要改变通知模式,我们可以创建一个新的实现并将其传递给Employee
。