乐趣区

关于设计原则:开放封闭原则不改代码怎么写新功能

再小的致力,乘以 365 都很显著

前言

上一篇文章,咱们讲了一个最根底的设计准则:繁多职责准则。这一讲,咱们来看下一个设计准则:凋谢关闭准则。

作为一名程序员,来了一个需要就要改一次代码,这种形式咱们曾经见怪不怪了,甚至曾经变成了一种下意识的反馈。批改也很容易,只有咱们依照之前的常规如法炮制就好了。

这是一种不费脑子的做法,却随同着长期的挫伤。每人每次都只改了一点点,然而,通过长期积攒,再来一个新的需要,改变量就要很大了。而在这个过程中,每个人都很无辜,因为每个人都只是遵循常规在批改。但后果是,所有人都受到了挫伤,代码越来越难以保护。

既然“批改”会带来这么多问题,那咱们能够不批改吗?凋谢关闭准则就提供了这样的一个新方向。

简介

凋谢关闭准则是这样表述的:

软件实体(类、模块、函数)应该对扩大凋谢,对批改关闭。

这个说法是 Bertrand Meyer 在其著述《面向对象软件结构》(Object-Oriented Software Construction)中提出来的,它给软件设计提出了一个极高的要求:不批改代码。

或者你想问,不批改代码,那我怎么实现新的需要呢?答案就是 靠扩大。用更艰深的话来解释,就是新需要应该用新代码实现。

凋谢关闭准则向咱们形容的是一个后果,就是咱们能够不批改代码而仅凭扩大就实现新性能。然而,这个后果的前提是要在软件外部留好扩大点,而这正是须要咱们去设计的中央。因为 每一个扩大点都是一个须要设计的模型

解释

举个例子,如果咱们正在开发一个酒店预订零碎,针对不同的用户,咱们须要计算出不同的房价。比方,普通用户是全价,金卡是 8 折,银卡是 9 折,代码写进去可能是这样的:

class HotelService {public double getRoomPrice(final User user, final Room room) {double price = room.getPrice();
    if (user.getLevel() == Level.GOLD) {return price * 0.8;}
    
    if (user.getLevel() == Level.SILVER) {return price * 0.9;}
    
    return price;
  }
}

这时,新的需要来了,要减少白金卡会员,给出 75 折的优惠,如法炮制的写法应该是这样的:

class HotelService {public double getRoomPrice(final User user, final Room room) {double price = room.getPrice();
    if (user.getLevel() == UserLevel.GOLD) {return price * 0.8;}
    
    if (user.getLevel() == UserLevel.SILVER) {return price * 0.9;}
    
    if (user.getLevel() == UserLevel.PLATINUM) {return price * 0.75;}
    
    return price;
  }
}

显然,这种做法就是批改代码的做法,每减少一个新的类型就要批改一次代码。然而,一个有各种级别用户的酒店零碎必定不只是房价有区别,提供的服务也可能有区别。可想而知,每减少一个用户级别,咱们要改的代码就铺天盖地。

那应该怎么办呢?咱们应该思考如何把它设计成一个能够扩大的模型。在这个例子外面,既然每次要减少的是用户级别,而且各种服务的差别都体现在用户级别上,咱们就须要一个用户级别的模型。在后面的代码里,用户级别只是一个简略的枚举,咱们能够给它丰盛一下:

interface UserLevel {double getRoomPrice(Room room);
}

class GoldUserLevel implements UserLevel {public double getRoomPrice(final Room room) {return room.getPrice() * 0.8;
  }
}

class SilverUserLevel implements UserLevel {public double getRoomPrice(final Room room) {return room.getPrice() * 0.9;
  }
}

咱们原来的代码就能够变成这样:

class HotelService {public double getRoomPrice(final User user, final Room room) {return user.getRoomPrice(room);
  }
}

class User {
  private UserLevel level;
  ...
  
  public double getRoomPrice(final Room room) {return level.getRoomPrice(room);
  }
}

这样一来,再减少白金用户,咱们只有写一个新的类就好了:

class PlatinumUserLevel implements UserLevel {public double getRoomPrice(final Room room) {return room.getPrice() * 0.75;
  }
}

之所以咱们能够这么做,是因为咱们在代码里留好了扩大点:UserLevel。在这里,咱们把原来的只反对枚举值的 UserLevel 升级成了一个有行为的 UserLevel。

通过这番革新,HotelService 的 getRoomPrice 这个办法就稳固了下来,咱们就不须要依据用户级别一直地调整这个办法了。至此,咱们就领有了一个稳固的结构块,能够在前期的工作中把它当做一个稳固的模块来应用。

当然,在这个例子里,这个办法是比较简单的。而在理论的我的项目中,业务办法都会比较复杂。

总结

明天,咱们讲了凋谢关闭准则,软件实体应该对扩大凋谢,对批改关闭。简略地说,就是不要批改代码,新的性能要用新的代码实现。

其实,情理大家都懂,但对很多人来说,做到是有难度的,尤其是在代码里留下扩大点,往往是须要有肯定设计能力的。很多优良的软件在设计上都给咱们提供了足够的扩大能力,向这些软件的接口学习,咱们能够学到更多的货色。

显然,要想提供扩大点,就须要面向接口编程。然而,是不是有了接口,就是好的设计了呢?下一讲,咱们来看设计一个接口还须要满足什么样的准则。

退出移动版