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判断, 需要修改原有逻辑。- 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)); }}
复制代码 为了避免更改原有逻辑,以下使用策略模式对代码进行重构,遵循开闭原则进行代码设计。- 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; }}
复制代码 三、里氏替换原则
子类对象能够替换程序中父类对象出现的任何地方,并且能够保证原来程序的逻辑行为不变及正确性不被破坏
面向对象编程语言中有多态的实现场景,多态的实现场景和里氏替换原则有点类似,但是他们关注的角度是不同的,多态是面向对象编程的特性,而里氏替换原则,是用来指导继承关系中子类该如何设计:子类的设计要确保在替换父类时候,不改变原有父类的约定。
具体实现中可以理解为,子类在设计的时候,要遵循父类的行为规定,父类定义的方法行为,子类可以改变方法的内部实现逻辑,但不能改变方法原有的接口约定。原有的行为约定包括:接口/方法 的声明,参数值,返回值,异常约定,甚至包括注释中所罗列的任何特殊说明。- 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接口给予具体的实现增加功能,具体的实现通过实现接口来获得功能。而具体的实现与框架之间并没有直接耦合。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |