找回密码
 立即注册
首页 业界区 安全 设计模式的前言——Solid设计原则

设计模式的前言——Solid设计原则

频鹏凶 2026-2-9 21:50:04
  Solid原则是为针对面向对象的程序语言设计,从本质上来讲,SOLID是5个原则的缩写,这5个原则有助于软件设计:更加容易理解,更灵活,可维护性更强。这个与掌握软件设计原理无关,这个原理是很多原则的子集。

  • 单一职责原则(Single responsibility principle)
  • 开闭原则(open-closed principle)
  • 里氏替换原则(liskov substitution principle)
  • 接口隔离原则(interface segregation principle)
  • 依赖倒置原则(dependency inversion principle)
一、单一职责原则

一个类或者一个模块只负责完成一个职责(A class or module should have a single reponsibility )。原则说,设计一个类时候,不要设计为大而全的类,要设计为粒度小,功能单一的类。

二、开闭原则

  软件实体(类,模块,方法等)应该对扩展开放,对修改关闭。通俗理解就是添加一个功能应该是在已有代码基础上进行扩展,而不是修改已有代码。
  以下代码违背了开闭原则,在新增用户类型后,要对用户类型进行if...else判断, 需要修改原有逻辑。
  1. import java.math.BigDecimal;public class OpenClosePrinciple {   public static void main(String[] args) {      OrderService orderService = new OrderService();      BigDecimal bigDecimal = orderService.calculateDiscount(2, BigDecimal.valueOf(100));      System.out.println(bigDecimal);   }}class OrderService{   //用数字(userType)判断用户类型   BigDecimal calculateDiscount(int userType,BigDecimal money){      BigDecimal result = null;      if (userType == 1){         result = calculateNormal(money);      } else if (userType == 2){         result = calculateVip(money);      } else if (userType == 3){         result = calculateSupVip(money);      } else if (userType == 4){         result = calculateTeamUser(money);      }      return result;   }   //不同的计算方式,写在一个类中,通过方法名来调用。以下为4种计算方式   private BigDecimal calculateNormal(BigDecimal money){      return money;   }   private BigDecimal calculateVip(BigDecimal money){      return money.multiply(BigDecimal.valueOf(0.8d));   }   private BigDecimal calculateSupVip(BigDecimal money){      return money.multiply(BigDecimal.valueOf(0.5d));   }   private BigDecimal calculateTeamUser(BigDecimal money){      return money.multiply(BigDecimal.valueOf(0.7d));   }}
复制代码
  为了避免更改原有逻辑,以下使用策略模式对代码进行重构,遵循开闭原则进行代码设计。
  1. import java.math.BigDecimal;import java.util.HashMap;import java.util.Map;public class OpenClosePrinciple {   public static void main(String[] args) {      //通过 枚举类的静态方法+枚举类的常量,获取数据。      //通过 调用策略控制器中的方法进行判断,来获取哪种class的策略类      DiscountStrategy strategy1 = StrategyContext.getStrategy(UserTypeEnum.getUserType(UserTypeEnum.VIP));      System.out.println(strategy1.calculateDiscount(BigDecimal.valueOf(100.0d)));      //通过 枚举类的常量,获取数据。      //通过 调用策略控制器中的方法进行判断,来获取哪种class的策略类      DiscountStrategy strategy2 = StrategyContext.getStrategy(UserTypeEnum.VIP.getUserType());      System.out.println(strategy2.calculateDiscount(BigDecimal.valueOf(100.0d)));   }}//用户类型枚举类enum UserTypeEnum {   NORMAL(1),   VIP(2);   private Integer userType;   UserTypeEnum(Integer userType) {      this.userType = userType;   }   public Integer getUserType() {      return userType;   }   public void setUserType(Integer userType){      this.userType = userType;   }   //枚举静态方法,通过哪一种枚举类型,来返回枚举中的数据   //该方法一般用于枚举中有两个数据的情况,此处为枚举中只有一个数据的情况   public static Integer getUserType(UserTypeEnum userTypeEnum){      //首先,对枚举中的所有类型进行遍历      for (UserTypeEnum u : UserTypeEnum.values()) {         if (u.equals(userTypeEnum)){            return u.getUserType();         }      }      return null;   }}//策略类型的接口interface DiscountStrategy{   BigDecimal calculateDiscount(BigDecimal money);}//普通打折的策略类class NormalDiscountStrategy implements DiscountStrategy{   @Override   public BigDecimal calculateDiscount(BigDecimal money) {      return money;   }}//VIP打折的策略类class VipDiscountStrategy implements DiscountStrategy{   @Override   public BigDecimal calculateDiscount(BigDecimal money) {      return money.multiply(BigDecimal.valueOf(0.8d));   }}//策略控制器,在初始化时候进行不同策略类型的保存。class StrategyContext{   private static Map strategyMap = new HashMap();   static{      strategyMap.put(UserTypeEnum.NORMAL.getUserType(), new NormalDiscountStrategy());      strategyMap.put(UserTypeEnum.VIP.getUserType(), new VipDiscountStrategy());   }   //通过在hashMap中查询策略类型,把对应策略类型的实例 返回。   public static DiscountStrategy getStrategy(Integer userType){      DiscountStrategy discountStrategy = strategyMap.get(userType);      return discountStrategy;   }}
复制代码
三、里氏替换原则

  子类对象能够替换程序中父类对象出现的任何地方,并且能够保证原来程序的逻辑行为不变及正确性不被破坏
  面向对象编程语言中有多态的实现场景,多态的实现场景和里氏替换原则有点类似,但是他们关注的角度是不同的,多态是面向对象编程的特性,而里氏替换原则,是用来指导继承关系中子类该如何设计:子类的设计要确保在替换父类时候,不改变原有父类的约定。
  具体实现中可以理解为,子类在设计的时候,要遵循父类的行为规定,父类定义的方法行为,子类可以改变方法的内部实现逻辑,但不能改变方法原有的接口约定。原有的行为约定包括:接口/方法 的声明,参数值,返回值,异常约定,甚至包括注释中所罗列的任何特殊说明。
  1. import java.util.HashMap;import java.util.Map;public class LiskovSubstitutionPrinciple {   public static void main(String[] args) {      //普通缓存实现//    CacheManager cacheManager = new CacheManager();//    UserService service = new UserService(cacheManager);      //redis缓存实现      RedisCentralCacheManager redisCacheManager = new RedisCentralCacheManager();      UserService service = new UserService(redisCacheManager);      String value1 = service.biz("key1");      String value2 = service.biz("key1");      String value3 = service.biz("key2");      String value4 = service.biz("key2");   }}class UserService{   //模拟db操作   private static Map db = new HashMap();      //定义缓存   private CacheManager cacheManager;   //利用static代码块: 模拟db存储数据   static{      db.put("key1","value1" );   }   //将实例化的缓存对象,引到这个对象中   public UserService(CacheManager cacheManager) {      this.cacheManager = cacheManager;   }   //查询数据    public String biz(String key){      //先从缓存中获取数据             String value = cacheManager.get(key);      //缓存中没有该条数据,去数据库查询数据            if (value == null){         value = dbData(key);         cacheManager.put(key,value);      }else{         System.out.println(key+"命中,执行业务操作:"+value);      }      return value;   }   //模拟数据库查询数据       private String dbData(String key){      String value = null;      value = db.get(key);      System.out.println("数据库中获取:  "+value);      return value;   }}//模拟:普通缓存查询和存放数据class CacheManager{   private Map cache = new HashMap();   public String get(String key){      String value = cache.get(key);      if (value == null || "".equals(value)){         return null;      }      return value;   }   public void put(String key,String value){      //此处定义规则:不用考虑数据库是否有该记录,即value是否为null,直接存放      cache.put(key,value );   }}//模拟:redis的集中缓存,存放数据,此处违背了里氏替换原则class RedisCentralCacheManager extends CacheManager{   private Map redisCache = new HashMap();   private static final String empty = "empty";   @Override   public String get(String key) {      String value = redisCache.get(key);      if (null == value || "".equals(value)){         return null;      }      return value;   }   //此处违背了里氏替换原则   @Override   public void put(String key, String value) {      //根据父类接口中定义的规则,直接存放,不要自定义数据存放。       if (value == null || "".equals(value)){         redisCache.put(key,empty );      }else{         redisCache.put(key, value);      }   }}
复制代码
执行结果(违背了里氏替换原则原则):

四、接口隔离原则

  对于接口来说:如果某个接口承担了与他无关的功能,则说该接口违背了接口隔离原则,可以把无关的接口剥离出去。
对于共同的代码来说:应该将代码的粒度细分出来,而不是定义一个大而全的接口,让子类被迫去实现它
五、依赖倒置原则(框架和容器中用的比较多)

  高层模块不要依赖低层模块,高层模块和低层模块应该通过抽象来互相依赖。除此之外,抽象不要依赖具体实现细节,具体实现细节依赖抽象。
  高层模块,从代码角度来说就是调用者,底层模块就是被调用者。调用者不要依赖于具体的实现,而应该依赖于抽象:如spring代码中的各种Aware接口,框架依赖于Aware接口给予具体的实现增加功能,具体的实现通过实现接口来获得功能。而具体的实现与框架之间并没有直接耦合。


来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册