什么是SOLID原则
SOLID = SRP(职责单一原则) + OCP(对扩展开发,修改关闭原则)+ LSP(里氏替换原则)+ ISP(接口隔离原则)+ DIP(依赖反转原则)
- SRP: Single Responsibility Principle,一个类或者模块只负责完成一个职责;
- OCP:Open Closed Principle,软件实体(模块、类、方法等)应该“对扩展开发,对修改关闭”;
- LSP:Liskov Substitution Principle,子对象能够替换程序中父类对象出现的任何地方,并且保证原来的程序逻辑行为不变及正确性不被破坏;
- ISP: Interface Segregation Principle,客户端应该不强迫依赖它不需要的接口;
- DIP: Dependency Inversion Principle,高层模块不要依赖底层模块,高层模块和底层模块之间应该通过抽象来相互依赖,除此之外,抽象不要依赖具体的实现细节,具体实现细节依赖抽象。
SOLID原则评判标准
SRP(职责单一原则)
评判标准
一个类或者模块只负责完成一个职责,不要设计大而全的类,要设计的粒度小、功能单一的类,也就是说一个类包含两个或者两个以上业务不相关的功能,就可以说它的职责不够单一。
- 类中的代码行数、函数或属性过多,影响代码的可读性和维护性,就需要考虑对类进行拆分;
- 类依赖其它类过多,或者依赖的类的其它类过多,不符合高内聚、低耦合的设计思想,就需要考虑对类就行拆分;
- 私有方法过多,考虑能否将私发方法到新的类中,设置为public方法,供更多的类使用,提高代码的复用性;
- 比较难给定一个适合的名字,很难用一个业务名词概括,说明职责定义的不够清晰;
- 类中大量的方法都是集中在操作类的几个属性,其它的属性就可以拆分出来。
代码示例
public class User
{
private String id;
private String userName;
private String pwd;
private String phone;
private String city;
private String province;
private String town;
private BigDecimal balance;
public int addUser(User user){
return 1;
}
public int deleteUser(String id){
return 1;
}
public List<User> queryUserByRoleId(String id){
return new ArrayList<>();
}
}
- User类对用户基本信息操作比较频繁,但是地址相关信息只在订单相关功能才用到city、province、town可以单独拆分成一个实体;
- User基本操作基本不用到账号余额,放到这里有些多余;
- addUser和deleteUser在一定场景下符合职责单一原则,但是业务扩展可能需要对删除做权限控制,就不太符合这个原则,没有一定的准则;
- queryUserByRoleId虽然查询的是用户相关信息,但引用了其它实体的内容,我们可以认为是职责不单一。
OCP(对扩展开发,修改关闭原则)
评判标准
一段代码是否易于扩展,某段代码在应未来需求变化的时候,能够做到“对外开放,对修改关闭”,那就说明这段代码的扩展性比较好。
添加一个新功能,不可能任何模块、类、方法不做修改。类需要创建、组装、并且做一些初始化操作,才能构建可运行的程序,修改这部分代码是再所难免的。我们要做的经量修改的时间操作更集中、更少、更上层、经量让最核心、最复杂的那部分逻辑代码满足开闭原则。
代码示例
public class Ocp {
public boolean pkgValidate(String userName, String phone){
if(StringUtils.isEmpty(userName)){
return false;
}
if(phone.length() == 1){
return false;
}
return true;
}
}
- 如果我们需要新增一个报文校验,就会对这个接口的代码做修改,对应的单元测试全部需要改;
- 这块属于校验的核心功能,每次改还要把之前的功能测试一遍;
示例代码重构
public class OcpUser
{
private String id;
private String userName;
private String pwd;
private String phone;
private BigDecimal balance;
//get set方法省略
}
-------------------------------------------------------------
public abstract class OcpHandler {
public abstract boolean check(OcpUser ocpUser);
}
-------------------------------------------------------------
public class PhoneOcpHandler extends OcpHandler{
@Override
public boolean check(OcpUser ocpUser) {
if(ocpUser.getPhone().length() == 1){
return false;
}
return true;
}
}
-------------------------------------------------------------
public class UserNameOcpHandler extends OcpHandler{
@Override
public boolean check(OcpUser ocpUser) {
if(StringUtils.isEmpty(ocpUser.getUserName())){
return false;
}
return true;
}
}
-------------------------------------------------------------
public class OcpC {
private List<OcpHandler> ocpHandlers = new ArrayList<>();
public void addAlertHandler(OcpHandler ocpHandler) { this.ocpHandlers.add(ocpHandler); }
public void check(OcpUser ocpUser) {
for (OcpHandler handler : ocpHandlers) {
handler.check(ocpUser);
}
}
}
-------------------------------------------------------------
public class ApplicationContext {
private OcpC ocpC;
// 饿汉式单例
private static final ApplicationContext instance = new ApplicationContext();
private ApplicationContext() {
instance.initializeBeans();
}
public static ApplicationContext getInstance() {
return instance;
}
public void initializeBeans() {
ocpC.addAlertHandler(new PhoneOcpHandler());
ocpC.addAlertHandler(new UserNameOcpHandler());
}
public OcpC getOcpC() {
return ocpC;
}
}
-------------------------------------------------------------
public class Demo {
public static void main(String [] args){
OcpUser ocpUser = new OcpUser();
ocpUser.setUserName("张三");
ocpUser.setPhone("13288888888");
ApplicationContext.getInstance().getOcpC().check(ocpUser);
}
}
- 现在扩展需要改OcpUser新增一个属性,需要校验的属性;
- 添加一个handler实现check;
- 在初始化initializeBeans新增一个handler;
- 在调用的时候加入对应的属性。
我们不能做到完全的对内扩展开放、对修改关闭,我们只可能在最上面抽象尽可能不该动原逻辑的前提下做扩展。
LSP(里氏替换原则)
评判标准
里式替换原则和多态类似,但是多态是实现方式,是面向对象的一个特性,是一种编程语言的语法,原则是规范,规范你怎么开发。
- 子类违背父类声明要实现的功能,比如说我父类从小到大排序,子类重新这个方法后是从大到小的顺序排序;
- 子类违背父类对出入、输出、异常约定,入参和出参类型一样,抛的异常类型也必须完全一样;
- 子类违背父类注释中所罗列的任何特殊说明,实现方法很父类注释方式说明不符。
代码示例
public class Parent {
public void sort(List<Integer> list){
//重小到大排序逻辑
}
public void query(String userName) throws NullPointerException{
//查询逻辑
}
/**
* 加1操作
* @param num
*/
public void add(int num){
num++;
}
}
public class Lsp extends Parent{
@Override
public void sort(List<Integer> list){
//重大道小排序逻辑
}
@Override
public void query(String userName) throws NotBoundException{
//查询逻辑
}
/**
* 加1操作
* @param num
*/
@Override
public void add(int num){
num--;
}
}
- sort排序的逻辑完全很父类相反;
- 抛出异常类型不一致;
- 和注解标识做不相关操作;
ISP(接口隔离原则)
评判标准
接口可以理解成三类,一组API接口集合(业务具体功能点)、单个API接口或者函数(类中的某一个方法)、OOP中的接口(Inteface)概念。
- 设计职责更加单一的接口,单一就意味着通用、复用性好;
- 某些类在实现接口的时候,需要重写一些不需要的方法,做了一些无用功。
代码示例
public interface CommonService {
public List<String> queryUserName(String status);
public int deleteUser(Long id);
public List<String> queryRoleName(String userName);
public int deleteRole(Long id);
}
public class RoleImpl implements CommonService{
@Override
public List<String> queryUserName(String status) {
return null;
}
@Override
public int deleteUser(Long id) {
return 0;
}
@Override
public List<String> queryRoleName(String userName) {
return null;
}
@Override
public int deleteRole(Long id) {
return 0;
}
}
public class UserImpl implements CommonService{
@Override
public List<String> queryUserName(String status) {
return null;
}
@Override
public int deleteUser(Long id) {
return 0;
}
@Override
public List<String> queryRoleName(String userName) {
return null;
}
@Override
public int deleteRole(Long id) {
return 0;
}
}
- 接口不灵活,用户和角色的接口放在一起要多做一个工作量
- 复用性很差
- 不利于代码拆分、管理
DIP(依赖反转原则)
评判标准
依赖反转原则也可以叫依赖倒置原则,高优先级的类不应该强行依赖低优先级的类,而是通过一个抽象类间接的操作,子类也基于抽象类做实现。
- 代码是否易于拆分,在代码重构的时候很难对模块之间解耦,就说明依赖关系太死;
- 低层次模块提供的接口要足够的抽象、通用,在设计时需要考虑高层次模块的使用种类和场景;
- 高层次模块没有依赖低层次模块的具体实现,方便低层次模块的替换。
代码示例
interface IWorker{
public void work();
}
class Worker implements IWorker{
public void work(){
// ... working
}
}
class SuperWorker implements IWorker{
public void work(){
// ... working much more
}
}
class Manager{
IWorker worker;
public void setWorker(IWorker w){
this.worker = w;
}
public void manage(){
this.worker.work();
}
}
- Manager具体实现不依赖Worker和SuperWorker类;
- 通过抽象接口IWorker相互依赖;
- 具体实现类依赖接口IWorker;
个人网站:http://www.cnzcb.cn/
公众号:
本文由博客一文多发平台 OpenWrite 发布!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。