首页 > Python基础教程 >
-
java教程之Java常见设计模式学习(非原创)(3)
最后我们来测试一下
public class test {
public static void main(String[] args) {
OrderPizza order1=new NewYorkOrderPizza();
}
}
最后我们再试试使用抽象工厂模式:
public interface FactoryPizza {
Pizza createPizza(String type);
}
单独将变化的工厂抽象
public class OrderPizza {
OrderPizza(FactoryPizza factory){
String type=null;
Pizza pizza=null;
do {
type=getType();
pizza=factory.createPizza(type);
if (pizza!=null) {
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}else{
break;
}
} while (true);
}
private String getType(){
String type=null;
BufferedReader br=null;
try {
br=new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza type:");
type= br.readLine();
} catch (IOException e) {
e.printStackTrace();
return "";
}
return type;
}
}
public class LondonFactoryPizza implements FactoryPizza{
@Override
public Pizza createPizza(String type) {
Pizza pizza=null;
if (type.equals("greek")) {
pizza=new LDGreekPizza();
}else if (type.equals("cheese")) {
pizza=new LDCheesePizza();
}else if (type.equals("pepper")) {
pizza=new LDPepperPizza();
}
return pizza;
}
}
public class NewYorkOrderPizza implements FactoryPizza{
@Override
public Pizza createPizza(String type) {
Pizza pizza=null;
if (type.equals("greek")) {
pizza=new NYGreekPizza();
}else if (type.equals("cheese")) {
pizza=new NYCheesePizza();
}else if (type.equals("pepper")) {
pizza=new NYPepperPizza();
}
return pizza;
}
}
效果和工厂方法模式是一样的,虽然这里可能看不出抽象工厂模式与工厂方法模式的差别,但是我们在大的项目就可以看出来了
四、单例模式
巧克力工厂项目,工厂只有一个锅炉,往锅炉中添加佐料,如果满了则其他人不能再次添加,或者必须倾倒后添加,然后将锅炉烧开,如果锅炉已经烧开就无需再次烧开,或者必须倾倒后再次烧开
好的,首先我们还是用普通的方式去做:
public class ChocolateFactor {
private boolean isEmpty;
private boolean isBoiled;
public ChocolateFactor() {
isEmpty=true;
isBoiled=false;
}
public void fill(){
if (isEmpty) {
isEmpty=false;
isBoiled=false;
System.out.println("锅炉满了");
}
}
public void boil(){
if ((!isEmpty)&&(!isBoiled)) {
isBoiled=true;
System.out.println("锅炉已经沸腾了");
}
}
public void dump(){
if ((!isEmpty)&&(isBoiled)) {
isEmpty=true;
isBoiled=false;
System.out.println("锅炉啥都没有咯");
}
}
}
然后我们测试下:
public class test {
public static void main(String[] args) {
ChocolateFactor factor=new ChocolateFactor();
factor.fill();
factor.boil();
ChocolateFactor factor1=new ChocolateFactor();
factor1.fill();
factor1.boil();
}
}
可以看到即使锅炉满了和沸腾了,其他人仍然可以往锅炉中添加,所以这是不合常理的,现在我们来用单例试试:
public class ChocolateFactor {
private boolean isEmpty;
private boolean isBoiled;
private static ChocolateFactor instance=null;
private ChocolateFactor() {
isEmpty=true;
isBoiled=false;
}
public static ChocolateFactor getInstance(){
if (instance==null) {
instance=new ChocolateFactor();
}
return instance;
}
public void fill(){
if (isEmpty) {
isEmpty=false;
isBoiled=false;
System.out.println("锅炉满了");
}
}
public void boil(){
if ((!isEmpty)&&(!isBoiled)) {
isBoiled=true;
System.out.println("锅炉已经沸腾了");
}
}
public void dump(){
if ((!isEmpty)&&(isBoiled)) {
isEmpty=true;
isBoiled=false;
System.out.println("锅炉啥都没有咯");
}
}
}
代码中可以看到我们将构造方法私有化,提供一个静态的方法去返回巧克力工厂实例,将巧克力工厂静态化,使其在内存中只保留一份。现在我们来测试下:
public class test {
public static void main(String[] args) {
ChocolateFactor factor=ChocolateFactor.getInstance();
factor.fill();
factor.boil();
ChocolateFactor factor1=ChocolateFactor.getInstance();
factor1.fill();
factor1.boil();
}
}
可以看到锅炉满了和沸腾之后对其再次操作是无效的。
但是其实这也是存在问题的,现在假设我们在多线程的状态,线程一走到了if (instance==null) { 后cpu不让它工作了,开始线程二工作,线程二将整个getInstance走完了,现在cpu又让线程1执行,这时候,线程1又会去new 一个新的巧克力工厂,这样又产生了和第一个方式同样的问题,
在线程中我们学过同步锁,我们可以简单的在:
public static synchronized ChocolateFactor getInstance(){
if (instance==null) {
instance=new ChocolateFactor();
}
return instance;
}
但是我们知道加入synchronized 是非常耗内存的,所以这也不是我们的最终解决办法,然后我们再次看到另一个解决办法:
public class ChocolateFactor {
private boolean isEmpty;
private boolean isBoiled;
// private static ChocolateFactor instance=null;
private static ChocolateFactor instance=new ChocolateFactor();
private ChocolateFactor() {
isEmpty=true;
isBoiled=false;
}
// public static synchronized ChocolateFactor getInstance(){
// if (instance==null) {
// instance=new ChocolateFactor();
// }
// return instance;
// }
public static synchronized ChocolateFactor getInstance(){
return instance;
}
public void fill(){
if (isEmpty) {
isEmpty=false;
isBoiled=false;
System.out.println("锅炉满了");
}
}
public void boil(){
if ((!isEmpty)&&(!isBoiled)) {
isBoiled=true;
System.out.println("锅炉已经沸腾了");
}
}
public void dump(){
if ((!isEmpty)&&(isBoiled)) {
isEmpty=true;
isBoiled=false;
System.out.println("锅炉啥都没有咯");
}
}
}
可以看到和之前的代码不同的是我们直接在定义变量的时候就直接将工厂new出来,然后在getInstance中直接返回,这种方式成为懒汉式,但是问题又来了,假如我们不需要使用getInstance,然后在使用这个的类的时候我们就已经在变量中直接将工厂new了出来,所以这样也是浪费了内存,那么我们的最终解决办法是:
public static synchronized ChocolateFactor getInstance(){
if (instance==null) {
synchronized (ChocolateFactor.class) {
if (instance==null) {
instance=new ChocolateFactor();
}
}
}
return instance;
}
使用双重判断加锁的方式,现在我们还是在多线程的状态,加入线程1还是在if (instance==null) { cpu不分配给它空间了,线程2开始工作,线程2一鼓作气将getInstance走完,现在线程1工作了,线程走到
if (instance==null) 方法instance已经不为null了,所以他将不会再次new 一个,所以这才是我们的终极方案。
五、其他模式
参考文章:
https://blog.csdn.net/qq_33750826/article/details/73558492
1. 装饰者模式
装饰着模式是一种开放-关闭原则的设计意义,开放即为以后设计添加子类方便,关闭即父类不用更改,具体查看参考文章的第四点
2. 命令模式
具体查看参考文章的第七点
3. 适配器模式
适配器通俗易懂相当于就是一个转换器,平时手机充电,假如这个头不能插入手机,我们只需要通过一个转换器,然后使用转换器上的插头即可。具体查看参考文章的第八点
4. 外观模式
具体查看参考文章的第九点
5. 模板模式
具体查看参考文章的第十点
6. 迭代器模式
具体查看参考文章的第十一点
7. 组合模式
具体查看参考文章的第十二点
8. 状态模式
具体查看参考文章的第十三点
9. 代理模式
具体查看参考文章的第十四点
10. 复合模式
具体查看参考文章的第十五点
11. 桥接模式
具体查看参考文章的第十六点
12. 生成器模式
具体查看参考文章的第十七点
13. 责任链模式
具体查看参考文章的第十八点
14. 解释器模式
具体查看参考文章的第二十点
15. 中介者模式
具体查看参考文章的第二十一点
16. 备忘录模式
具体查看参考文章的第二十二点
17. 原型模式
具体查看参考文章的第二十三点
18. 访问者模式
具体查看参考文章的第二十四点
六、设计模式总结
1. 什么是设计模式
模式:在某些场景下,针对某类问题的某种通用解决方案
场景:项目环境
问题:约束条件,项目目标等
解决方案:通用、可以复用的设计,解决约束,达到目标
2. 设计模式的三个分类
2.1 创建型模式
对象实例化的模式,创建型模式解耦了对象的实例化过程:
简单工厂:一个工厂类根据传入的参量决定创建出哪一种产品类的实例
工厂方法:定义一个创建对象的接口,让子类决定实例化哪一个类
抽象工厂:创建相关或依赖对象的家族,而无需明确指定具体类
单例模式:某个类只能有一个实例,提供一个全局访问点
生成器模式:封装一个复杂对象的构建过程,并可以按步骤构造
原型模式:通过复制现有的实例来创建新的实例
2.2 结构型模式
把类或对象结合在一起形成更大的结构:
适配器模式:将一个类的方法接口转换成客户希望的另外一个接口
组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构
装饰模式:动态地给对象添加新的功能
代理模式:为其他对象提供一个代理以控制对这个对象的访问
蝇量模式:通过共享技术有效地支持大量细粒度的对象
外观模式:提供统一的方法来访问子系统的一群接口
桥接模式:将抽象部分与它的实现部分分离,使它们都可以独立地变化
2.3 行为型模式
类和对象如何交互,及划分责任和算法:
模板模式:定义一个算法结构,而将一些步骤延迟到子类中实现
解释器模式:给定一个语言, 定义它的文法的一种表示,并定义一个解释器
策略模式:定义一系列的算法,把它们封装起来, 并且使它们可相互替换
状态模式:允许一个对象在其内部状态改变时改变它的行为
观测者模式:对象间的一对多的依赖关系
备忘录模式:在不破坏封装性的前提下,保存对象的内部状态
中介者模式:用一个中介对象来封装一系列的对象交互
命令模式:将命令请求封装为一个对象,使得可用不同的请求来进行参数化
访问者模式:在不改变数据结构的前提下,增加作用于一组对象元素新的功能
责任链:请求发送者和接收者之间解耦,使的多个对象都有机会处理这个请求
迭代器:一种遍历访问聚合对象中各个元素的方法,不暴露该对象的内部结构
3. 设计模式的六大原则
3.1 组合复用原则
多用组合,少用继承
找到变化部分,抽象,封装变化
区分“Has-A”与“Is-A”如果HasA表示你有它应该使用组合,IsA表示你是它应该使用继承。
3.2 依赖倒置原则
依赖:成员变量、方法参数、返回值
要依赖于抽象,不要依赖于具体
高层模块不应该依赖低层模块,二者都应该依赖其抽象
抽象不应该依赖具体,具体应该依赖抽象
针对接口编程,不要针对实现编程
以抽象为基础搭建的结构比具体类搭建的结构要稳定的多
在java中,抽象指的是接口或者抽象类,具体就是具体的实现类
3.3 开闭原则
对扩展开放,对修改关闭
通过扩展已有软件系统,可以提供新的功能
修改的关闭,保证稳定性和延续性
3.4 迪米特法则
一个对象应该与其他对象保持最少的了解。只与直接朋友交谈。
成员变量、方法参数、方法返回值中需要的类为直接朋友
类与类之间的关系越密切了解越多,耦合度越大
尽量降低类与类之间的耦合
外观模式、中介者模式
接口隔离原则:一个类对另一个类的依赖应该建立在最小的接口上
3.5 里氏替换原则
所有引用基类的地方必须能透明地使用其子类对象
子类在扩展父类功能时不能破坏父类原有的功能
使用继承时,遵循里氏替换原则:
子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
当子类重载父类方法时,方法的形参要比父类方法的参数更宽松
当子类实现父类的抽象方法时,方法的返回值要比父类更严格
里氏替换原则是设计整个继承体系的原则
3.6 单一职责原则
类应该只有一个导致类变更的理由
即一个类只负责一项职责
降低类的复杂度
提高系统的可维护性
修改时降低风险溢出
4. 模式使用思考
4.1 保持简单
尽可能用最简单的方式解决问题
简单而弹性的设计,一般使用模式是最好的方法
4.2 设计模式非万能
模式是通用问题的经验总结
使用模式时要考虑它对其他部分的影响
不需要预留任何弹性的时候,删除掉模式
平衡与妥协
4.3 何时需要模式
找出设计中会变化的部分,通常就是需要考虑模式的地方
重构时
4.4 重构的时间就是模式的时间
重构就是改变代码来改进组织方式的过程
利用模式来重构
七、参考文章
https://blog.csdn.net/qq_33750826/article/details/73558492