案例分析:设计模式与代码的结构特性
为什么要使用设计模式
因为我们的项目的需求是永远在变的,为了应对这种变化,使得我们的代码能够轻易的实现解耦和拓展。如果能够保证代码一次写好以后都不会再改变了,那可以想怎么写怎么写了。
如何判断那里需要使用设计模式

我选择的设计模式是门面模式。
门面模式
一、定义
门面模式也叫外观模式,是一种比较常见的封装模式,定义如下:
一个子系统的外部与其内容的通信必须通过一个统一的对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。
门面模式注重“统一“,也就是提供一个访问子系统的接口,除了这个接口不允许有任何访问子系统的行为发生。
二、门面模式认识
门面模式的角色:
Facade门面角色:客户端可以调用这个角色的方法,此角色知晓子系统的所有功能和职责。一般情况下,本角色会将所有客户端发来的请求委派到响应的子系统,也就是该角色没有实际的业务逻辑,只是一个委托类。
subsystem子系统:可以有一个或多个子系统,每个子系统都不是一个单独的类,而是一个类的集合,子系统并不知道门面的存在,对于子系统而言,门面就是一个客户端而已。
其通用类图如下:

在例子中一般结构图如下:

三、门面模式应用
下面看门面模式的一个代码示例:
//子系统
public class ClassA{
public void doSomethingA(){
}
}
public class ClassB{
public void doSomethingB(){
}
}
public class ClassC{
public void doSomethingC(){
}
}
//门面对象
public class Facade{
//被委托的对象
private ClassA a = new ClassA();
private ClassB b = new ClassB();
private ClassC c = new ClassC();
//提供给外部访问的方法
public void methodA(){
this.doSomethingA();
}
public void methodB(){
this.doSomethingB();
}
public void methodC(){
this.doSomethingC();
}
}
四、门面模式优点
1、子系统可以有选择的暴露方法
门面模式还有一个附带的好处,就是能够有选择性地暴露方法。一个模块中定义的方法可以分成两部分,一部分是给子系统外部使用的,一部分是子系统内部模块之间相互调用时使用的。有了Facade类,那么用于子系统内部模块之间相互调用的方法就不用暴露给子系统外部了。
2、一个门面模式可以有很多个门面类
在门面模式中,通常只需要一个门面类。如果一个系统有好几个子系统的话,每一个子系统都有一个门面类,整个系统可以有数个门面类。
3、不能为子系统增加新行为
门面模式的用意是为子系统提供一个集中化和简化的沟通管道,而不能向子系统加入新的行为。就比如包工头的作用只是调度其他人工作的,但是自己不工作。
4、松耦合、而且使用简单。
用户与子系统解耦,屏蔽子系统;可以提高子系统的独立性;并且客户类不需要知道子系统的内部构造。
四、门面模式缺点
门面模式最大缺点就是不符合开闭原则,对修改关闭,对扩展开放,一旦系统投产后发现问题,继承和覆写都不顶用,只能修改门面对象源码,风险较大,需要谨慎对待。
五、使用场景与注意事项
使用场景:
为一个复杂的模块或子系统提供一个对外访问的接口
子系统相对独立--外界访问只需要黑箱操作即可
预防低水平人员带来的风险扩散
注意事项:
1.一个子系统可以有多个门面
一般情况下,一个门面只有一个门面就够了,但在一下情况下可以有多个门面,
门面已经庞大到不能忍受的程度:按照功能拆分为多个门面是个不错的原则
子系统提供不同的访问路径
2.门面不参与子系统的业务逻辑
以上述通用源代码为例,把门面类上的methodC改一下,让他先调用ClassA的方法,再调用ClassC的方法。
接下来把门面类上的methodC改一下,让他先调用ClassA的方法,再调用ClassC的方法:
//修改门面
public class Facade{
//被委托的对象
private ClassA a = new ClassA();
private ClassB b = new ClassB();
private ClassC c = new ClassC();
//提供给外部访问的方法
public void methodA(){
this.a.doSomethingA();
}
public void methodB(){
this.b.doSomethingB();
}
//加入业务逻辑
public void methodC(){
this.a.doSomethingA();
this.c.doSomethingC();
}
}
上述门面对象中已经参与了业务逻辑,这样设计是非常不靠谱的,为什么呢?因为门面对象只是提供了一个访问子系统的一个路径而已,不应该参与具体的业务逻辑,否则会出现颠倒依赖的问题:子系统必须依赖门面才能访问,这是一个设计的错误!不仅违反单一职责原则,同时也破坏了系统的封装性。那么应该怎么处理呢?建立一个封装类,封装完毕后提供给门面对象。我们先建立一个封装类:
//封装类
public class context{
//委托处理
private ClassA a = new ClassA();
private ClassC c = new ClassC();
//业务逻辑、复杂的计算
public void complexMethod(){
this.a.methodA();
this.c.methodC();
}
}
//门面类
public class Facade {
//被委托的对象
private ClassA a = new ClassA();
private ClassB b = new ClassB();
private Context context = new Context();
//提供给外部访问的方法
public void methodA(){
this.a.doSomethingA();
}
public void methodB(){
this.b.doSomethingB();
}
public void methodC(){
this.context.complexMethod();
}
}
这样一次封装后,门面对象就不会参与到业务逻辑里了,在门面模式中,门面角色应该是稳定的,他不应该经常变化,一旦系统运行就不应该再被改变,它是子系统对外提供的接口,变来变去还怎么保证其他模块的稳定呢?所有的变化都应该被封装在子系统内部,无论怎么变化,在外界看到还是同一个方法,同一个门面,这才是最好的架构。
注:与代理模式的区别:这个模式乍一看还真的很像代理模式,其实还是有很大的区别的。
比如说他们都引入了中介,起到了代理的功能。但是代理模式只代理一个类,而且代理类与原类实现相同的抽象。门面类就不一样了,他代理的是一系列类,与子系统可以有不同的抽象。
六、小结
设计模式是我们一定要了解的东西,熟悉设计模式能让我们设计出易于扩展和维护的代码结构。但是并不是任何地方都需要上设计模式,应该结合我们的项目实际进行分析是否需要设计模式,使用哪种设计模式。
https://github.com/remainjjh/grr

浙公网安备 33010602011771号