面向对象设计原则
我们在进行软件开发时,不仅仅需要将最基本的业务给完成,还要考虑整个项目的可维护性和可复用性,我们开发的项目不单单需要我们自己来维护,同时也需要其他的开发者一起来进行共同维护,因此我们在编写代码时,应该尽可能的规范。
7大面向对象设计原则
单一职责原则(Single Responsibility Principe,SRP)
-
是最简单的面向对象设计原则,它用于控制类的大小
-
定义:一个对象应该只包含单一的职责,并且该职责被完整地封装在一个类中
class Coder{ public void coding(){ System.out.println("程序员会编程") } } class Worker{ public void work(){ System.out.println("工人会打螺丝"); } } class Rider{ public void ride(){ System.out.println("骑手会送外卖"); } }
开闭原则(Open Close Principle,OCP)
-
定义:软件实体应当对扩展开发,对修改关闭
-
将一个行为抽象成一个统一的接口或是抽象类,具体代码由不同的对象去实现
public abstract class Coder{ public abstract void coding(); class JavaCoder extends Coder{ @Override public void coding(){ SYstem.out.println("Java太卷了,快去学Go吧!"); } } class PHPCoder extends Coder{ @Override public void coding(){ SYstem.out.println("PHP是世界上最好的语言!"); } } class CCoder extends Coder{ @Override public void coding(){ SYstem.out.println("笑死,Java再牛逼底层不还得是我?"); } } }
里氏替换原则(Liskov Substitution Principle,LSP)
-
是对子类型的特别定义,它由芭芭拉.利斯科夫(Barbara Liskov)在1987年在一次会议上名为“数据的抽象与层次”的演说中首先提出
-
定义:所有引用基类的地方必须能透明地使用其子类的对象
-
简单地说就是,子类可以扩展父类的方法,但不能改变父类原有的功能
-
子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
-
子类可以增加自己特有的方法。
-
当子类的方法重载父类的方法时,方法的前置条件(即方法的输入/入参)要比父类方法的输入参数更宽松
-
当子类的方法实现父类的方法时(重写/重载或实现抽象方法),方法的后置条件(即方法的输出/返回值)要比父类更严格或与父类一样
-
public abstract class Coder{ public void coding(){ System.out.println("我会打代码"); } class JavaCoder extends Coder{ public void game(){ System.out.println("子类除了会打代码之外,还会打游戏!"); } } }
可以看到JavaCoder虽然继承自Coder,但是并没有对父类方法进行重写,并且还在父类的基础上进行额外扩展,符合里氏替换原则。
-
最佳优化
public abstract class People{ public abstract void coding(); //这个行为还是定义出来,但是不实现 class Coder extends People{ @Override public void coding(){ System.out.println("我会打代码"); } } class JavaCoder extends People{ public void game(){ System.out.println("子类除了会打代码之外,还会打游戏!"); } public void coding(){ System.out.println("摆烂了,啊对对对"); } } }
-
里氏替换也是实现开闭原则的重要方式之一。
依赖倒转原则(Dependence Inversion Principle,DIP)
-
最明显的是Spring框架
-
定义:高层模块不应该依赖于低层模块,它们都应该依赖抽象,抽象不应该依赖于细节,细节应该依赖于抽象。
public class Main{ public static void main(String[] args){ UserController controller = new UserCOntroller(); } interface UserMapper{ //接口中只能做CRUD方法 } static class UserMapperImpl implements UserMapper{ //实现类完成CRUD具体实现 } interface UserService{ //业务代码定义.... } static class UserServiceImpl implements UserService{ @Resource //现在由Spring来为我们选择一个指定的实现类,然后注入,而不是由我们在类中硬编码进行指定 UserMapper mapper; //业务代码具体实现 } static class UserController{ @Resource UserService service; //直接使用接口,就算改实现,也不需要再修改代码 //业务代码 } }
可以看到,通过使用接口,我们就可以将原有的强关联给弱化,我们只需要知道接口中定义了什么方法然后去使用即可,而具体的操作有接口的实现类来完成,并由Spring来为我们注入,而不是我们通过硬编码的方式去指定
接口隔离原则(Interface Segregation Principle,ISP)
-
实际上是读接口的细化
-
定义:客户不应该依赖那些它不需要的接口
interface SmartDevice{ //智能设备才有getCpu和getMemory String getCpu(); String getType(); String getMemory(); } interface NormalDevice{ //智能设备只有getType String getType(); } //电脑就是一种电子设备,那么就继承此接口 class Computer implements SmartDevice{ @Override public String getCpu(){ return "i9-12900K"; } @Override public String getType(){ return "电脑"; } @Override public String getMemory(){ return "32G DDR5"; } } //风扇也是一种电子设备 class Fan implements NormalDevice{ @Override public String getType(){ return "风扇"; } }
合成复用原则(Composite Reuse Principle,CRP)
-
核心就是委派
-
定义:优先使用对象组合,而不是通过继承来达到复用的目的
class A{ public void connectDatabase(){ System.out.println("我是连接数据库操作!"); } } class B{ //不进行继承,而是在用的时候给一个A,当然也可以抽象成一个接口,更加灵活 public void test(A a){ System.out.println("我是B的方法,我也需要连接数据库!"); a.connectDatabase(); //在通过传入的对象A去执行 } }
或是
class A{ public void connectDatabase(){ System.out.println("我是连接数据库操作!"); } } class B{ A a; public B(A a){ //在构造时就指定好 this.a = a; } public void test(){ System.out.println("我是B的方法,我也需要连接数据库!"); a.connectDatabase(); //也是通过对象A去执行 } }
通过对象之间的组合,大大降低了类之间的耦合度
迪米特法则(Law of Demeter)
-
又称最少知识原则,是对程序内部数据交互的限制
-
定义:每一个软件单位对其他单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位
public class Main{ public static void main(String[] args) throws IOException{ Socket socket = new Socket("localhost",8080); Test test = new Test(); test.test(socket.getLocalAddress().getHostAddress()); //在外面解析 } static class Test{ public void test(String str){ //一个字符串就能搞定,就没必要丢整个对象进来 System.out.println("IP地址:" + str); } } }