结构型
关于设计模式介绍 23种设计模式之创建型有讲述
上篇文章讲解了5种创建型模式,这章将讲解7种结构型模式。
七种结构型模式
适配器模式,装饰模式,代理模式,外观模式,桥接模式,组合模式,享元模式。
其中对象的适配器模式是各种模式的起源,看图分析:
1.适配器模式(Adapter)
概念
适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题
主要分为三大类
类的适配器模式,对象的适配器模式,接口的适配器模式。
类的适配器模式
看图分析:
核心思想:有一个Source类,拥有一个方法,待适配,目标接口是Targetable,通过Adapter类,将Source的功能扩展到Targetable里。
代码演示:
Source:
package com.myy.adapter;
public class Source {
public void method1() {
System.out.println("这是最初的method1");
}
}
Targetable:
package com.myy.adapter;
public interface Targetable {
//与原类中的方法相同
public void method1();
//新类的方法
public void method2();
}
Adapter类继承Source类,实现Targetable接口:
package com.myy.adapter;
public class Adapter extends Source implements Targetable{
@Override
public void method2() {
// TODO Auto-generated method stub
System.out.println("这是Targetable的method1");
}
}
测试类AdapterTest:
package com.myy.adapter;
public class AdapterTest {
public static void main(String[] args) {
Targetable target = new Adapter();
target.method1();
target.method2();
}
}
运行结果:
这样Targetable接口的实现类就具有了Source类的功能
对象的适配器模式
基本思路和类的适配器模式相同,只是将Adapter类做修改,这次不继承Source类,而是持有Source类的实例,以达到解决兼容性的问题。看图:
代码演示:
只要修改Adapter类的源码即可:
package com.myy.adapter;
public class Wrapper implements Targetable{
private Source source;
public Wrapper(Source source) {
super();
this.source = source;
}
public void method2() {
System.out.println("这是Targetable的method1");
}
public void method1() {
source.method1();
}
}
测试:
package com.myy.adapter;
public class AdapterTest {
public static void main(String[] args) {
Source source = new Source();
Targetable target = new Wrapper(source);
target.method1();
target.method2();
}
}
运行结果:
和第一种一样,只是适配的方法不同而已。
接口的适配器模式
概念
有时我们写一个接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,这明显有时比较浪费,因为并不是所有的方法都是我们需要的,有时只需要某一些,此处为了解决这个问题,我们引入了接口的适配器模式,借助于一个抽象类,该抽象类实现了该接口,实现了所有的方法,而我们不和原始的接口打交道,只和改抽象类取得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行。看类图:
在实际开发中,我们也常会遇到这种接口中定义了太多的方法,以至于有时我们在一些实现类中并不是都需要。
代码演示:
Sourceable :
package com.myy.adapter;
public interface Sourceable {
public void method1();
public void method2();
}
抽象类Wrapper2 :
package com.myy.adapter;
public abstract class Wrapper2 implements Sourceable{
public void method1() {}
public void method2() {}
}
SourceSub1 :
package com.myy.adapter;
public class SourceSub1 extends Wrapper2{
public void method1() {
System.out.println("这是Sourceable 接口的第一个Sub1");
}
}
SourceSub2 :
package com.myy.adapter;
public class SourceSub2 extends Wrapper2{
public void method2() {
System.out.println("这是Sourceable 接口的第二个 Sub2");
}
}
WrapperTest:
package com.myy.adapter;
public class WrapperTest {
public static void main(String[] args) {
Sourceable source1 = new SourceSub1();
Sourceable source2 = new SourceSub2();
source1.method1();
source1.method2();
source2.method1();
source2.method2();
}
}
运行结果:
三种适配器模式的应用场景
类的适配器模式
当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原有的类,实现新的接口即可。
对象的适配器模式
当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个Wrapper类,持有原类的一个实例,在Wrapper类的方法中,调用实例的方法就行。
接口的适配器模式
当不希望实现一个接口中所有的方法时,可以创建一个抽象类Wrapper,实现所有方法,我们写别的类的时候,继承抽象类即可.
2.装饰模式(Decorator)
概念
在不改变原来类的情况下,进行扩展。
装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。
关系图如下:
Source类时被装饰类,Decorator类是一个装饰类,可以为Source类动态的添加一些功能,代码如下:
Sourceable :
package com.myy.Decorator;
public interface Sourceable {
public void method();
}
Source :
package com.myy.Decorator;
public class Source implements Sourceable{
@Override
public void method() {
System.out.println("这是最初的method");
}
}
Decorator :
package com.myy.Decorator;
public class Decorator implements Sourceable{
private Sourceable source;
public Decorator(Sourceable source) {
super();
this.source = source;
}
@Override
public void method() {
System.out.println("之前的Decorator");
source.method();
System.out.println("之后的Decorator");
}
}
测试类DecoratorTest :
package com.myy.Decorator;
public class DecoratorTest {
public static void main(String[] args) {
Sourceable source = new Source();
Sourceable obj = new Decorator(source);
obj.method();
}
}
运行结果:
装饰器模式的应用场景
1.需要扩展一个类的功能
2.动态的为一个对象增加功能,而且还能动态撤销。(继承不能做到这一点,继承的功能是静态的,不能柜台增删)
缺点:产生过多相似的对象,不易排除
3.代理模式(Proxy)
概念
其实每个模式名称就表明了该模式的作用,**代理模式就是多一个代理类出来,替原对象进行一些操作。比如我们在租房的时候会去找中介,为什么呢?因为你对该地区房屋的信息掌握的不够全面,希望找一个更熟悉的人去帮你做,此处的代理就是这个意思。再如我们有时候打官司,我们需要请律师,因为律师在法律方面有专长,可以替我们进行操作,表达我们的想法。
关系图:
代码演示:
Sourceable :
package com.myy.proxy;
public interface Sourceable {
public void method();
}
Sourceable:
package com.myy.proxy;
public class Proxy implements Sourceable{
private Source source;
public Proxy(){
super();
this.source = new Source();
}
public void method() {
before();
source.method();
after();
}
private void after() {
System.out.println("之后 proxy!");
}
private void before() {
System.out.println("之前 proxy!");
}
}
测试类ProxyTest:
package com.myy.proxy;
public class ProxyTest {
public static void main(String[] args) {
Sourceable source = new Proxy();
source.method();
}
}
运行结果:
代理模式的应用场景
如果已有方法在使用的时候需要对原有的方法进行改进,此时两种方法:
1.修改原有的方法来适应。这样违反了“对扩展开发,对修改关闭”的原则
2.就是采用一个代理类调用原有的方法,且对产生的结果进行控制。这种方法就是代理模式。
使用代理模式,可以将功能划分的更加清晰,有助于后期维护。
4.桥接模式(Bridge)
概念
这个模式使用的并不多,但思想确实很普通。就是要分离抽象部分与现实部分。
实现弱关联,即在运行时才产生依赖关系。
降低代码之间的耦合。
生活中桥接模式
就拿汽车在路上行驶的来说。即有小汽车又有公交汽车,它们都不但能在市区中的各路上行驶,也能在高速公路上行驶。这你会发现,对于交通工具(汽车)有不同的类型,然而它们所行驶的环境(路)也在变化,在软件系统中适应两个方面的变化?怎样实现才能应对这种变化?
使用场景
- 不希望在抽象和它的实现部分之间有一个固定的绑定关系
- 抽象部分以及实现部分都通过子类生成一定的扩充内容
- 对一个抽象的实现部分的修改对客户不产生影响
在以下情况应当使用桥接模式
- 如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的联系
- 设计要求实现化角色的任何改变不应当影响客户端,或者说实现化角色的改变对客户端是完全透明的
- 一个构件有多于一个的抽象角色和实现化角色,系统需要它们之间进行动态耦合
- 虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两种。
代码演示:
抽象道路
package com.myy.bridge;
public abstract interface AbstractRoad {
public AbstractCar car=null;
public void setCar(AbstractCar car);
public abstract void Run();
}
高速公路
package com.myy.bridge;
public class SpeedWay implements AbstractRoad{
@Override
public void setCar(AbstractCar car) {
// TODO Auto-generated method stub
car=car;
}
@Override
public void Run() {
// TODO Auto-generated method stub
car.Run();
System.out.println("----行驶在高速公路上-----");
}
}
市区街道
package com.myy.bridge;
public class Street implements AbstractRoad{
@Override
public void setCar(AbstractCar car) {
// TODO Auto-generated method stub
car=car;
}
@Override
public void Run() {
// TODO Auto-generated method stub
car.Run();
System.out.println("----行驶在市区街道上---");
}
}
抽象汽车
package com.myy.bridge;
public abstract interface AbstractCar {
public abstract void Run();
}
小汽车
package com.myy.bridge;
public class Car implements AbstractCar{
@Override
public void Run() {
// TODO Auto-generated method stub
System.out.println("---小汽车----");
}
}
公共汽车:
package com.myy.bridge;
public class Bus implements AbstractCar{
@Override
public void Run() {
// TODO Auto-generated method stub
System.out.println("----公共汽车----");
}
}
客户端:
package com.myy.bridge;
public class BridgeTest {
public static void main(String[] args) {
AbstractRoad road1 = new SpeedWay();
road1.setCar(new Car());
road1.Run();
AbstractRoad road2=new Street();
road2.setCar(new Bus());
road2.Run();
}
}
桥接模式是一个非常有用的模式,也非常复杂,它很好的符合了开放-封闭原则和优先使用对象,而不是继承这两个面向对象原则。
5.组合模式(Composite)
概念
使对象组合成树形的结构。使用户对单个对象和组合对象的使用具有一致性。
组合模式角色
-
抽象构件角色(Component):为组合中的对象声明接口, 在适当的情况下,也可实现所有类共有接口的缺省行为。
-
树叶构件角色(Leaf):在组合中表示叶节点对象,没有子节点,实现抽象构件角色声明的接口。
-
树枝构件角色(Composite):在组合中表示分支节点对象,有子节点,实现抽象构件角色声明的接口;存储子部件
组合模式场景
- 表示对象的 部分-整体 层次结构
- 忽略组合对象与单个对象的不同,统一的使用组合结构中的所有对象
组合模式两种实现方式
-
透明式的组合模式:将管理子构件的方法定义在Component接口中,这样Leaf类就需要处理这些对其意义不大的方法(空实现或抛异常),在接口层次上Leaf和Composite没有区别,即透明性。Component 声明的这些管理子构件的方法对于Leaf来说是不适用的,这样也就带来了一些安全性问题。
-
安全式的组合模式:将管理子构件的方法定义在Composite中,这样编译时任何从Leaf 中增加或删除对象的尝试都将被发现,但是由于Leaf和Composite有不同的接口(方法),又失去了透明性
代码演示:
安全的组合模式
这种组合模式,叶子节点,也就是单个对象不具有对象的控制功能。仅仅有简单的业务操作。
Component :
package com.myy.composite;
public interface Component {
Composite getCmposite();
void sampleComposite();
}
Leaf :
package com.myy.composite;
public class Leaf implements Component{
@Override
public Composite getCmposite() {
return null;
}
@Override
public void sampleComposite() {
// TODO Auto-generated method stub
System.out.println("Leaf operation");
}
}
Composite :
package com.myy.composite;
import java.util.ArrayList;
import java.util.List;
public class Composite implements Component{
List<Component> list = new ArrayList<Component>();
@Override
public Composite getCmposite() {
// TODO Auto-generated method stub
return this;
}
@Override
public void sampleComposite() {
// TODO Auto-generated method stub
System.out.println("Composite operation");
for (Component com : list) {
com.sampleComposite();
}
}
public void add(Component component) {
list.add(component);
}
public void remove(Component component) {
list.remove(component);
}
}
CompositeTest :
package com.myy.composite;
public class CompositeTest {
public static void main(String[] args) {
Component leaf1 = new Leaf();
Component leaf2 = new Leaf();
Component composite = new Composite();
composite.getCmposite().add(leaf1);
composite.getCmposite().add(leaf2);
composite.getCmposite().sampleComposite();
}
}
运行结果:
透明组合模式
这种组合模式,叶子节点与组合对象具有相同的方法,外表看来毫无差异。不过叶子节点的处理方法默认为空。忽略叶子节点,与组合对象的差异性。
Component:
package com.myy.composite;
public interface Component {
Composite getCmposite();
void sampleComposite();
public void add(Component component) ;
public void remove(Component component);
}
Leaf:
package com.myy.composite;
public class Leaf implements Component{
@Override
public Composite getCmposite() {
return null;
}
@Override
public void sampleComposite() {
// TODO Auto-generated method stub
System.out.println("Leaf operation");
}
@Override
public void add(Component component) {
// TODO Auto-generated method stub
}
@Override
public void remove(Component component) {
// TODO Auto-generated method stub
}
}
Composite:
package com.myy.composite;
import java.util.ArrayList;
import java.util.List;
public class Composite implements Component{
List<Component> list = new ArrayList<Component>();
@Override
public Composite getCmposite() {
// TODO Auto-generated method stub
return this;
}
@Override
public void sampleComposite() {
// TODO Auto-generated method stub
System.out.println("Composite operation");
for (Component com : list) {
com.sampleComposite();
}
}
public void add(Component component) {
list.add(component);
}
public void remove(Component component) {
list.remove(component);
}
}
CompositeTest:
package com.myy.composite;
public class CompositeTest {
public static void main(String[] args) {
Component leaf1 = new Leaf();
Component leaf2 = new Leaf();
Component composite = new Composite();
composite.getCmposite().add(leaf1);
composite.getCmposite().add(leaf2);
composite.getCmposite().sampleComposite();
}
}
运行结果:
6.外观模式(Facade)
概念
它隐藏了系统的复杂性,并向客户提供了一个可以访问系统的接口。这种类型的设计模式属于结果型模式。为子系统中的一组接口提供了一个统一的访问接口,这个接口使得子系统更容易被访问或者使用
意图
外观模式主要是为了为一组接口提供一个一致的界面。从而使得复杂的子系统与用户端分离解耦。
有点类似家庭常用的一键开关,只要按一个键,台灯卧室客厅的灯都亮了。虽然他们各有各自的开关,但是对外用一个来控制。
应用场景
- 为复杂系统提供简单的接口
- 客户程序与抽象类的实现部分分离
- 构建层次系统时,用作入口
优点
- 由于Facade类封装了各个模块交互的过程,如果今后内部模块调用关系发生了改变,只需要修改Facade实现就可以了
- Facade实现是可以被多个客户端调用的
ModuleA:
package com.myy.facade;
public class ModuleA {
public void testFuncA() {
System.out.println("This is Function From 改变了");
}
}
ModuleB:
package com.myy.facade;
public class ModuleB {
public void testFuncB() {
System.out.println("This is Function From ModuleB");
}
}
Facade:
package com.myy.facade;
public class Facade {
private ModuleA moduleA = null;
private ModuleB moduleB = null;
private static Facade mFacade = null;
private Facade(){
moduleA = new ModuleA();
moduleB = new ModuleB();
}
public static Facade getInstance() {
if(mFacade == null) {
mFacade = new Facade();
}
return mFacade;
}
public void testOperation() {
moduleA.testFuncA();
moduleB.testFuncB();
}
}
FacadeTest :
package com.myy.facade;
public class FacadeTest {
public static void main(String arg[]) {
Facade.getInstance().testOperation();
}
}
运行结果:
7.享元模式(Flyweight)
享元的目的是为了减少不会要额内存消耗,将多个对同一对象的访问集中起来,不必为每个访问者创建一个单独的对象,以此来降低内存的消耗。
代码演示:
FlyWeight :
package com.myy.flyweight;
import java.util.HashMap;
import java.util.Map;
public class FlyWeight {
static class MyString {
private String myChar;
public MyString(String myChar) {
this.myChar = myChar;
}
public void display() {
System.out.println(myChar);
}
}
static class MyCharacterFactory {
private Map<String, MyString> pool;
public MyCharacterFactory() {
pool = new HashMap();
}
public MyString getMyCharacte(String strig) {
MyString myString = pool.get(strig);
if (myString == null) {
myString = new MyString(strig);
pool.put(strig, myString);
}
return myString;
}
}
public static void main(String[] args) {
MyCharacterFactory myCharacterFactory = new MyCharacterFactory();
MyString a = myCharacterFactory.getMyCharacte("a");
MyString b = myCharacterFactory.getMyCharacte("b");
MyString a1 = myCharacterFactory.getMyCharacte("a");
MyString d = myCharacterFactory.getMyCharacte("d");
if (a == a1) {
System.out.println("true");
}
}
}
运行结果:
【本章先论述结构型,只供参考。如有错误,欢迎留言。】