设计模式是为了让程序具有更好的:
在编程时应当遵守的原则,也是各种设计模式的基础、依据。
Open Closed Principle。编程中最基础、最重要的设计原则。 开闭原则规定软件中的对象(类,模块,函数等等)应该对于扩展是开放的,但是对于修改是封闭的。
Single responsibility principle。对类来说,一个类应该只负责一项职责。
如类A
负责两个不同的职责:职责1
、职责2
。当职责1
需求变更而改变类A
时,可能造成职责2
执行错误。所以需要将类A
的粒度按职责分解为A1
,A2
。
xpackage fun.hara;
public class Demo {
public static void main(String[] args) {
new Vehicle("汽车").run();
new Vehicle("摩托车").run();
}
}
class Vehicle{
private String name;
public Vehicle(String name) {
this.name = name;
}
public void run(){
System.out.println(name + "运行在公路上");
}
}
Vehicle
的run()
方法处理了不同交通工具的运行。即有多项职责。run()
方法可能会造成其他交通工具的运行错误。
或者,我们新增一种交通工具,如飞机,很明显当前run()
方法不能兼容,而去修改run()
方法可能又会带来一系列新的问题。xxxxxxxxxx
package fun.hara;
public class Demo {
public static void main(String[] args) {
new CarVehicle("汽车").run();
new MotorcycleVehicle("摩托车").run();
new AirplaneVehicle("飞机").run();
}
}
class CarVehicle{
private String name;
public CarVehicle(String name) {
this.name = name;
}
public void run(){
System.out.println(name + "运行在公路上");
}
}
class MotorcycleVehicle{
private String name;
public MotorcycleVehicle(String name) {
this.name = name;
}
public void run(){
System.out.println(name + "不允许上路");
}
}
class AirplaneVehicle{
private String name;
public AirplaneVehicle(String name) {
this.name = name;
}
public void run(){
System.out.println(name + "飞翔在天空上");
}
}
v1.0.0
的问题。v3.0.0
的方式。xxxxxxxxxx
public class Demo{
public static void main(String[] args) {
new Vehicle("汽车").carRun();
new Vehicle("摩托车").motorcycleRun();
new Vehicle("飞机").airplaneRun();
}
}
class Vehicle{
private String name;
public Vehicle(String name) {
this.name = name;
}
public void carRun(){
System.out.println(name + "运行在公路上");
}
public void motorcycleRun(){
System.out.println(name + "不允许上路");
}
public void airplaneRun(){
System.out.println(name + "飞翔在天空上");
}
}
单一职责原则:
Interface Segregation Principle ISP指明客户(client)不应该依赖于它不使用的方法。ISP把非常庞大臃肿的接口拆分成为更小的和更具体的接口,这样客户将会只需要知道他们感兴趣的方法。这种缩小的接口也被称为角色接口(role interfaces)。 ISP的目的是解耦,从而容易重构,更改和重新部署。
xxxxxxxxxx
public class Demo{
public static void main(String[] args) {
// main
}
// 只需要实现A接口的method1,method3方法
public static void doWork1(A a){
// doWork2
}
// 只需要实现A接口的method2方法
public static void doWork2(A a){
// doWork2
}
}
interface A{
void method1();
void method2();
void method3();
}
doWork1()
和doWork2()
方法都依赖了A
接口,但他们仅需要A
接口的部分方法。
此时A
接口的设计显然不合理,因为对于客户端,我们想要调用doWork1()
和doWork2()
方法都要去实现不需要的方法。A
接口进行拆分。在实际开发中,接口可能是按角色来拆分的。xxxxxxxxxx
public class Main{
public static void main(String[] args) {
// main
}
// 只需要实现A接口的method1,method3方法
public static void doWork1(A1 a){
// doWork2
}
// 只需要实现A接口的method2方法
public static void doWork2(A2 a){
// doWork2
}
}
interface A1{
void method1();
void method3();
}
interface A2{
void method2();
}
interface A extends A1, A2{
}
v1.0.0
的问题。
另外,若存在使用method1()
,method2()
,method3()
三个方法的需要。可额外定义一个接口来继承方法对应的接口。Dependency inversion principle DIP 是指一种特定的解耦(传统的依赖关系创建在高层次上,而具体的策略设置则应用在低层次的模块上)形式,使得高层次的模块不依赖于低层次的模块的实现细节,依赖关系被颠倒(反转),从而使得低层次模块依赖于高层次模块的需求抽象。
该原则规定:
图1中,高层对象A依赖于底层对象B的实现;图2中,把高层对象A对底层对象的需求抽象为一个接口A,底层对象B实现了接口A,这就是依赖反转。
xxxxxxxxxx
public class Demo{
public static void main(String[] args) {
new Worker().doWork(new JapaneseTool());
}
}
class Worker{
public void doWork(JapaneseTool tool){
tool.screwdriver("螺丝钉1");
tool.screwdriver("螺丝钉2");
}
}
class JapaneseTool{
public void screwdriver(String screw){
System.out.println("使用日本螺丝刀拧螺丝钉:" + screw);
}
}
Worker
视为高层的类,JapaneseTool
视为底层的类,Worker
依赖了JapaneseTool
,非常紧密的耦合。若我们需要将JapaneseTool
,替换成ChineseTool
,则要去修改Worker
的源代码。
难道每次替换都要去修改Worker
的源代码吗?doWork()
方法依赖于该接口,而不是依赖具体的实现类。类似于JDBC
。xxxxxxxxxx
public class Demo{
public static void main(String[] args) {
new Worker().doWork(new JapaneseTool());
new Worker().doWork(new ChineseTool());
}
}
class Worker{
public void doWork(ITool tool){
tool.screwdriver("螺丝钉1");
tool.screwdriver("螺丝钉2");
}
}
interface ITool{
void screwdriver(String screw);
}
class JapaneseTool implements ITool{
public void screwdriver(String screw){
System.out.println("使用日本螺丝刀拧螺丝钉:" + screw);
}
}
class ChineseTool implements ITool{
public void screwdriver(String screw){
System.out.println("使用国产螺丝刀拧螺丝钉:" + screw);
}
}
DIP:
Liskov Substitution Principle。 里氏替换原则中说,任何基类可以出现的地方,子类一定可以出现。
子类可以扩展父类的功能,但不能改变父类原有的功能。即子类尽量不要重写父类的方法。
如父类存在add(int a, int b)
方法,是将a
和b
相加,而子类重写后,将a
和b
相减。此时若用子类去替换父类对象,就会出错。
LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。
如此,问题产生了:“我们如何去度量继承关系的质量?”
Liskov于1987年提出了一个关于继承的原则“Inheritance should ensure that any property proved about supertype objects also holds for subtype objects.”——“继承必须确保超类所拥有的性质在子类中仍然成立。”
也就是说,当一个子类的实例应该能够替换任何其超类的实例时,它们之间才具有is-A
关系。
来源:百度百科 我们来研究一下LSP的实质。学习OO的时候,我们知道,一个对象是一组状态和一系列行为的组合体。状态是对象的内在特性,行为是对象的外在特性。LSP所表述的就是在同一个继承体系中的对象应该有共同的行为特征。 这一点上,表明了OO的继承与日常生活中的继承的本质区别。
于是,工程师们开始关注如何确保对象的行为。1988年,B. Meyer提出了Design by Contract(契约式设计)理论。DbC从形式化方法中借鉴了一套确保对象行为和自身状态的方法,其基本概念很简单:
以上是单个对象的约束条件。为了满足LSP,当存在继承关系时,子类中方法的前置条件必须与超类中被覆盖的方法的前置条件相同或者更宽松;而子类中方法的后置条件必须与超类中被覆盖的方法的后置条件相同或者更为严格 一些OO语言中的特性能够说明这一问题: 继承并且覆盖超类方法的时候,子类中的方法的可见性必须等于或者大于超类中的方法的可见性,子类中的方法所抛出的受检异常只能是超类中对应方法所抛出的受检异常的子类。 以上这些特性都非常好地遵循了LSP。但是DbC呢?很遗憾,主流的面向对象语言(不论是动态语言还是静态语言)还没有加入对DbC的支持。但是随着AOP概念的产生,相信不久DbC也将成为OO语言的一个重要特性之一
xxxxxxxxxx
public class Demo{
public static void main(String[] args) {
A a1 = new A();
A a2 = new B();
System.out.println(a1.add(1,2));
System.out.println(a2.add(1,2));
}
}
class A{
public int add(int a, int b){
return a + b;
}
}
class B extends A{
public int add(int a, int b){
return a - b;
}
}
B
类所覆盖的add()
方法破坏了该行为在父类中的含义。在实际开发中,可能是无意的。insert()
方法(希望这个方法不是你写的#滑稽),然而这个方法竟然帮你执行了删除操作!!Law of Demeter。迪米特法则又叫作最少知识原则(Least Knowledge Principle,LKP),就是说一个对象应当对其他对象有尽可能少的了解,不和陌生人说话。
public
方法,不对外泄露任何信息。Composite Reuse Principle。原则是尽量使用合成、聚合的方式,而不是使用继承。
异步、削峰、解耦
面对接口Rt(ResponseTime响应时间)有要求的场景,则必须进行优化。
在下单流程中,优惠券扣除、积分增减,短信发送等流程,实际是没有先后顺序要求的,即可以同时进行。
疑问:好像多线程也能解决该问题?为什么要上消息队列呢?