JAVA设计模式---1-5

设计模式的用处:

先假设一个需求:使用java语言设计一个计算器程序,要求接收控制台输入的两个数字和一个运算符号(+ - * /)中的一个。你会怎么设计?

是不是也是开始一个main方法,然后“请输入第一个数字” ---“请输入第二个数字” --请输入运算符

--判断输入的运算符,然后根据运算符输出结果的形式呢?

程序没有任何问题,但是程序存在的最大问题复用性问题,如果修改需求是否需要修改源码呢?如果让你用面向对象的思想去做呢?或许你会定义一个运算符Operator类,里面定义一个方法getResult(),将两个数字和运算符都传进去,直接获取结果。这样完美的解决了复用的问题,也体现了面向对象的思想,但是如果有一天我想加一个开根计算呢?是否又需要修改源码了呢?或者重新定义一个? 我们可以想想java的三大特性,封装、继承、多态。我们上面的解决思路只是用了一个封装的特性,剩下两个没有用到,那么如何通过继承和多态完成该程序的复用性和解耦合呢?如何避免在变需求的时候恨不得跳楼似的把代码全部推倒重新去写呢?这就需要我们通过设计模式来解耦了

下面讲第一个简单的模式---工厂模式

一、简单的工厂模式

上面说了,怎么用继承和多态来对代码进行解耦合,增强复用性和扩展性呢?我们可以将运算符封装成一个类,类里面两个属性 第一次输入的数字和第二次输入的数字,提供get和set方法,并定义一个默认的getResult方法;

每一个运算符专门定义一个类,继承上面父类,重写计算结果方法,如下图

最后提供一个工厂类来根据输入的符号返回对应的对象实例

在main方法中测试

以后无论多少需求,只需要增加一个类,在工厂方法中增加一个实例即可。

 

二、策略模式

同样先提个需求,做个超市收银的系统,根据每个物品的单价和数量来计算出最后的总价格。(后期可能会加需求)

脑子里是不是想着用上面的工厂模式去做呢? 创建个共同的父类,

定义一个获取最终结果的方法,子类去继承

然后通过工厂类获取实例对象,最后输出结果。

完全可以,但是现在超市双十一搞活动,满200便宜50,同时根据购物金额的大小有不同的打折,8.7.6.5折,这时候我们就发现客户端的代码太臃肿了,这时候就用到策略模式。

 

策略模式是定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实例不同,它可以以相同的方式调用不同的算法,减少了各类算法类与使用算法类之间的耦合。

 

将结账对象注入到Context中,同时构造方法限定了必须传入条件才可以,按所传入的条件选择合适的结账算法,

最终的测试端代码如下

在客户端的代码只剩下两行,对于客户端只需要明确类型和Context对象即可,剩下的算法由Context来进行选择,甚至连工厂都已经不用了,客户端并不知道某个超类的存在。

 

三、单一职责原则

一个类而言,应该仅有一个引起它变化的原因

    简单来说,一个类最好做一件事,当它负责的功能多了,就不可避免的会造成耦合度太高,牵一发动全身。建议将相同逻辑相似功能的都独立成类

    比如写一个俄罗斯方块,要借助java图形化来构建,还要写业务逻辑判断是否移动,监控按键并作出相应的响应。那这个时候就要考虑单一职责原则,将业务逻辑和图形化业务分离开来,如果某天要从pc端移植到手机端,只需要修改图形化的具体代码即可,业务逻辑代码完全可以直接使用。

软件设计真正要做的许多内容,就是发现职责并把职责分别归类,判断一个类是否需要分离处理啊,就应该考虑如果你多于一个动机去改变这个类,那么这个类就多一个职责

四、开放-封闭原则

简单来说,是说软件实体应该可以被扩展,但是不能被修改,对于扩展是开放的,对于更改是封闭的。

设计程序要尽量去满足这一条件,但是绝对不是强制必须达到这个条件,因为需求总是不固定的,没有完美的软件设计,这条原则是要求我们在第一次需求更改后,将自己代码进行重构,再遇到类似的需求就可以直接扩展。主要是对代码以后的扩展性做一个提前的预想。

五、依赖倒转

原话解释是抽象不应该依赖细节,细节应该依赖抽象。

说白了就是面对接口编程,不要面对实现编程。