在具体学习设计模式之前,先要了解什么是设计模式,以及为什么要学习设计模式。
简单来说,设计模式就是一套被人们反复应用,形成了一套代码编写规范的经验以及结构总结。我们通过设计模式的学习,可以有意识地将业务逻辑与界面逻辑做到有效的分离,使程序的耦合度降低,让程序变得易维护、易扩展、易复用、少修改。
这里还要提一句的是,我们在用面向对象语言编写代码时,应该注意点几点准则:
1、单一指责原则:就一个类而言,引起它的变化的因素越少越好,最好只有一个因素。因为一个类承担过多的职责,相当于把这些职责耦合在了一起,一个职责的变化可能削弱或抑制它完成其他的职责。当业务发生变化时,设计会受到意想不到的破坏。直接例子就是会导致我们修改的工作量提升)
2、开放-封闭原则:软件实体(类,模块,函数等)应该可以扩展,但是不可以修改。也可以理解为,对扩展是开放的,宽松的;对修改是关闭的,严格的。这条原则也是面向对象设计的核心所在。当然,完全对修改封闭也是不现实的,但是通过遵守这一原则设计出的程序,可以减少我们修改程序时的工作量。
3、依赖倒转原则:简单来说,就是我们应该针对接口进行编程,不应该针对实现。例如:当我们生产一台电脑时,再设计内存条插槽时,只需要遵守它的设计规范就好,组装时,凡事遵守该规范的内存条均可使用,无需知道具体会用到哪种品牌的。如果针对实现设计,那我们就要事先知道用哪种品牌的内存条,这大大增加了我们设计开发时的工作量。
4、里氏代换原则:子类型必须能够替代他们的父类型。这条也是多台概念的体现。
以上4条原则,也充分体现了面向对象语言的特性,即继承,封装,多态。
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
现在我们开始来看第一个设计模式,也是类创建性模式中的简单工厂模式,又叫静态工厂方法模式
何为简单工厂模式,就是书写一个类作为工厂类,它负责创建相应具体的产品类对象,并将结果return给客户端。客户端只需要给他传递相应的条件,即可拿到需要的产品对象,而无需知道工厂类是如何创建这个对象的。
假如说,我们需要编写一个计算器程序,先实现简单的加减法,对于初学面向对象语言的人来说,可能会这样书写代码:
在客户端的页面类中
String operaStr = "-";
double a = 2;
double b = 1;
double result = 0.0;
switch (operaStr) {
case "+":
result = a + b;
break;
case "-":
result = a - b;
break;
}
System.out.println(result);
首先,这样是可以实现功能,但是这样设计程序,使客户端页面不仅仅承担了数据的获取与展示职责,也承担了运算的职责,当数据获取出现问题时,会导致其运算职责受到影响。同样运算出现问题时,也可能导致数据展示受到影响。
其次,当我们需要增加乘法与除法运算功能时,我们要修改这个类,像这样
String operaStr = "-";
double a = 2;
double b = 1;
double result = 0.0;
switch (operaStr) {
case "+":
result = a + b;
break;
case "-":
result = a - b;
break;
case "*":
result = a * b;
break;
case "/":
result = a / b;
break;
}
System.out.println(result);
有人会说,这样修改很简单啊,一下子就完成了。那如果你的程序中,有多个地方拥有这个运算功能,那你是不是就要修改所有这些地方了。如果你的功能更加复杂,那修改起来是不是更加麻烦。这种设计是否增加了工作量。
现在我们用简单工厂模式来设计这个程序
1、首先创建一个运算父类
public abstract class Operation {
public double a = 0.0;
public double b = 0.0;
public abstract double getResult();
}
2、创建具体的运算类,加法运算类,减法运算类
//加法运算类
public class OperationAdd extends Operation {
@Override
public double getResult() {
return a + b;
}
}
//减法运算类
public class OperationSub extends Operation {
@Override
public double getResult() {
return a - b;
}
}
3、运算工厂类
public class OperationFactory {
public static Operation creatOperation(String str) {
Operation operation = null;
switch (str) {
case "+":
operation = new OperationAdd();
break;
case "-":
operation = new OperationSub();
break;
}
return operation;
}
}
4、客户端页面
Operation operation = OperationFactory.creatOperation("+");
operation.a = 1.0;
operation.b = 2.0;
double result = operation.getResult();
有人会说,一个计算器弄得这么麻烦,没有看到这样写的优势啊。别急,我们一起来分析分析。
首先,我将客户端数据获取、展示,运算进行了分离,这样满足了单一职责原则。客户端只负责与用户的交互,如获取用户命令,展示运算结果。运算类只负责运算,不关心其他职责。当一方有问题时,降低了对其他类的影响。
其次,当我需要增加乘法与除法运算时,我只需要仿照加法与减法,增加两个运算类OperationMul和OperationDiv,并在运算工厂类中稍作修改
public class OperationFactory {
public static Operation creatOperation(String str) {
Operation operation = null;
switch (str) {
case "+":
operation = new OperationAdd();
break;
case "-":
operation = new OperationSub();
break;
case "*":
operation = new OperationMul();
break;
case "/":
operation = new OperationDiv();
break;
}
return operation;
}
}
对于客户端而言,我们即使不进行修改,也可实现新的功能,不管有多少调用,均不会受到修改的影响,直接降低了功能变化造成的工作量,耦合度降低。当再次添加其他运算功能时,只需添加新的运算类,对运算工厂类稍作修改,无需改动已有的运算类。这种低耦合度的设计,降低了修改过程中,对已有功能的影响。是不是体现了易维护、易扩展、易复用、少修改的原则。