这里我们主要介绍7种结构型模式:适配器模式、装饰模式、代理模式、外观模式、桥接模式、组合模式、享元模式。其中对象的适配器模式是各种模式的起源,我们看下面的图:

B1、适配器模式(Adapter)

  模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问,使得原本由于接口不兼容而不能一 起工作的那些类可以一起工作。主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式。

  [1] Target (这是客户所期待的接口。目标可以是具体的或抽象的类,也可以是接口)。

    class Target
{
public virtual void Request()
{
Console.WriteLine("普通请求!");
}
}

  [2] Adaptee (需要适配的类)。

    class Adaptee
{
public void SpecificRequest()
{
Console.WriteLine ("特殊请求!");
}
}

  [3] Adapter (通过在内部包装一个Adaptee对象,把源接口转换成目标接口)。

    class Adapter:Target
{
private Adaptee adaptee = new Adaptee(); public override void Request()
{
adaptee.SpecificRequest() ;
}
}

  [4] 客户端调用

        static void Main(string[] args)
{
Target target = new Adapter();
target.Request();
Console.Read();
}

(1)、类的适配器模式

核心思想就是:有一个Source类,拥有一个方法,待适配,目标接口是Targetable,通过Adapter类,将Source的功能扩展到Targetable里,看代码:

  1. public class Source {
  2. public void method1() {
  3. System.out.println("this is original method!");
  4. }
  5. }
  1. public interface Targetable {
  2. /* 与原类中的方法相同 */
  3. public void method1();
  4. /* 新类的方法 */
  5. public void method2();
  6. }
  1. public class Adapter extends Source implements Targetable {
  2. @Override
  3. public void method2() {
  4. System.out.println("this is the targetable method!");
  5. }
  6. }

Adapter类继承Source类,实现Targetable接口,下面是测试类:

  1. public class AdapterTest {
  2. public static void main(String[] args) {
  3. Targetable target = new Adapter();
  4. target.method1();
  5. target.method2();
  6. }
  7. }

输出:

this is original method! this is the targetable method!

这样Targetable接口的实现类就具有了Source类的功能。

(2)、对象的适配器模式

基本思路和类的适配器模式相同,只是将Adapter类作修改,这次不继承Source类,而是持有Source类的实例,以达到解决兼容性的问题。看图:

只需要修改Adapter类的源码即可:

  1. public class Wrapper implements Targetable {
  2. private Source source;
  3. public Wrapper(Source source){
  4. super();
  5. this.source = source;
  6. }
  7. @Override
  8. public void method2() {
  9. System.out.println("this is the targetable method!");
  10. }
  11. @Override
  12. public void method1() {
  13. source.method1();
  14. }
  15. }

测试类:

  1. public class AdapterTest {
  2. public static void main(String[] args) {
  3. Source source = new Source();
  4. Targetable target = new Wrapper(source);
  5. target.method1();
  6. target.method2();
  7. }
  8. }

输出与第一种一样,只是适配的方法不同而已。

(3)、接口的适配器模式

第三种适配器模式是接口的适配器模式,接口的适配器是这样的:有时我们写的一个接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,这明显有时比较浪费,因为并不是所有的方法都是我们需要的,有时只需要某一些,此处为了解决这个问题,我们引入了接口的适配器模式,借助于一个抽象类,该抽象类实现了该接口,实现了所有的方法,而我们不和原始的接口打交道,只和该抽象类取得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行。看一下类图:

这个很好理解,在实际开发中,我们也常会遇到这种接口中定义了太多的方法,以致于有时我们在一些实现类中并不是都需要。看代码:

  1. public interface Sourceable {
  2. public void method1();
  3. public void method2();
  4. }

抽象类Wrapper2:

  1. public abstract class Wrapper2 implements Sourceable{
  2. public void method1(){}
  3. public void method2(){}
  4. }
  1. public class SourceSub1 extends Wrapper2 {
  2. public void method1(){
  3. System.out.println("the sourceable interface's first Sub1!");
  4. }
  5. }
  1. public class SourceSub2 extends Wrapper2 {
  2. public void method2(){
  3. System.out.println("the sourceable interface's second Sub2!");
  4. }
  5. }
  1. public class WrapperTest {
  2. public static void main(String[] args) {
  3. Sourceable source1 = new SourceSub1();
  4. Sourceable source2 = new SourceSub2();
  5. source1.method1();
  6. source1.method2();
  7. source2.method1();
  8. source2.method2();
  9. }
  10. }

测试输出:

the sourceable interface's first Sub1! the sourceable interface's second Sub2!

达到了我们的效果!

  总结一下三种适配器模式的应用场景:

  类的适配器模式:当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原有的类,实现新的接口即可。

  对象的适配器模式:当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个Wrapper类,持有原类的一个实例,在Wrapper类的方法中,调用实例的方法就行。

  接口的适配器模式:当不希望实现一个接口中所有的方法时,可以创建一个抽象类Wrapper,实现所有方法,我们写别的类的时候,继承抽象类即可。

B2、装饰模式(Decorator):动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。

  要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例,关系图如下:

Source类是被装饰类,Decorator类是一个装饰类,可以为Source类动态的添加一些功能,代码如下:

  1. public interface Sourceable {
  2. public void method();
  3. }
  1. public class Source implements Sourceable {
  2. @Override
  3. public void method() {
  4. System.out.println("the original method!");
  5. }
  6. }
  1. public class Decorator implements Sourceable {
  2. private Sourceable source;
  3. public Decorator(Sourceable source){
  4. super();
  5. this.source = source;
  6. }
  7. @Override
  8. public void method() {
  9. System.out.println("before decorator!");
  10. source.method();
  11. System.out.println("after decorator!");
  12. }
  13. }

测试类:

  1. public class DecoratorTest {
  2. public static void main(String[] args) {
  3. Sourceable source = new Source();
  4. Sourceable obj = new Decorator(source);
  5. obj.method();
  6. }
  7. }

输出:

before decorator! the original method! after decorator!

装饰器模式的应用场景:

1、需要扩展一个类的功能。

2、动态的为一个对象增加功能,而且还能动态撤销。(继承不能做到这一点,继承的功能是静态的,不能动态增删。)

缺点:产生过多相似的对象,不易排错!

B3、代理模式(Proxy)

  为其他对象提供一种代理以控制对访问对象的访问。

Proxy模式为其他对象提供一种代理以控制对这个对 象的访问。可简单理解为,代理模式就是多一个代理类出来,替原对象进行一些操作,比如我们在租房子的时候回去找中介,为什么呢?因为你对该地区房屋的信息掌握的不够全面,希望找一个更熟悉的人去帮你做,此处的代理就是这个意思。先来看看关系图

根据上文的阐述,代理模式就比较容易的理解了,我们看下代码:

  1. public interface Sourceable {
  2. public void method();
  3. }
  1. public class Source implements Sourceable {
  2. @Override
  3. public void method() {
  4. System.out.println("the original method!");
  5. }
  6. }
  1. public class Proxy implements Sourceable {
  2. private Source source;
  3. public Proxy(){
  4. super();
  5. this.source = new Source();
  6. }
  7. @Override
  8. public void method() {
  9. before();
  10. source.method();
  11. atfer();
  12. }
  13. private void atfer() {
  14. System.out.println("after proxy!");
  15. }
  16. private void before() {
  17. System.out.println("before proxy!");
  18. }
  19. }

测试类:

  1. public class ProxyTest {
  2. public static void main(String[] args) {
  3. Sourceable source = new Proxy();
  4. source.method();
  5. }
  6. }

输出:

before proxy! the original method! after proxy!

代理模式的应用场景:

如果已有的方法在使用的时候需要对原有的方法进行改进,此时有两种办法:

1、修改原有的方法来适应。这样违反了“对扩展开放,对修改关闭”的原则。

2、就是采用一个代理类调用原有的方法,且对产生的结果进行控制。这种方法就是代理模式。

使用代理模式,可以将功能划分的更加清晰,有助于后期维护!

B4、外观模式(Facade)

  为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

  个人理解:定义一个外观类,外观类将客户端的处理请求分配给适当的子系统对象。

  [1] 定义两个子系统类

    class SubSystemOne
{
public void MethodOne()
{
Console.WriteLine("子系统方法一");
}
} class SubSystemTwo
{
public void MethodTwo()
{
Console.WriteLine("子系统方法二");
}
}

  [2] 定义外观类

    public class Facade
{
SubSystemOne one;
SubSystemTwo two; public Facade()
{
one = new SubSystemOne();
two = new SubSystemTwo();
} public void MethodA()
{
Console.WriteLine ("\n 方法组 A()----");
one.MethodOne();
two.MethodTwo();
} public void MethodB()
{
Console .WriteLine ("\n 方法组 B()----");
two.MethodTwo();
one.MethodOne();
}
}

  [3] 客户端调用

        static void Main(string[] args)
{
Facade facade = new Facade();
facade.MethodA();
facade.MethodB();
Console.Read();
}

  首先,在设计初期阶段,应该要有意识的将不同的两个层分离,比如经典 的三层架构。层的层与层之间建立外观 Facade,这样可以为复杂的子系统提供一个简单的接口,使得耦合大大降低。

  其次,在开发阶段,子系 统往往因为不断的重构演化而变得越来越复杂,大多数的模式使用时也都会产生很多很小的类,増加外观Facade可以提供一个简单的接口,减少它们之间的依赖。

  第三,在维护一个遗留的大型系统时,可能这个系统已经非常难以维护和扩展了,可以为新系统开发一个外观Facade类,来提供设计粗糙或高度复杂的遗留代码的比较清晰 简单的接口,让新系统与Facade对象交互,Facade与遗留代码交互所有复杂的工作。

  Facade模式为子系统中的一组接口提供一个一致的界面,此模 式定义了一个高层接口,这个接口使得这一子系统更加容易使用。解决类与类之间的依赖关系,像spring一样,可以将类和类之间的关系配置到配置文件中,而外观模式就是将他们的关系放在一个Facade类中,降低了类与类之间的耦合度,该模式中没有涉及到接口,看下类图:(我们以一个计算机的启动过程为例)

我们先看下实现类:

  1. public class CPU {
  2. public void startup(){
  3. System.out.println("cpu startup!");
  4. }
  5. public void shutdown(){
  6. System.out.println("cpu shutdown!");
  7. }
  8. }
  1. public class Memory {
  2. public void startup(){
  3. System.out.println("memory startup!");
  4. }
  5. public void shutdown(){
  6. System.out.println("memory shutdown!");
  7. }
  8. }
  1. public class Disk {
  2. public void startup(){
  3. System.out.println("disk startup!");
  4. }
  5. public void shutdown(){
  6. System.out.println("disk shutdown!");
  7. }
  8. }
  1. public class Computer {
  2. private CPU cpu;
  3. private Memory memory;
  4. private Disk disk;
  5. public Computer(){
  6. cpu = new CPU();
  7. memory = new Memory();
  8. disk = new Disk();
  9. }
  10. public void startup(){
  11. System.out.println("start the computer!");
  12. cpu.startup();
  13. memory.startup();
  14. disk.startup();
  15. System.out.println("start computer finished!");
  16. }
  17. public void shutdown(){
  18. System.out.println("begin to close the computer!");
  19. cpu.shutdown();
  20. memory.shutdown();
  21. disk.shutdown();
  22. System.out.println("computer closed!");
  23. }
  24. }

User类如下:

  1. public class User {
  2. public static void main(String[] args) {
  3. Computer computer = new Computer();
  4. computer.startup();
  5. computer.shutdown();
  6. }
  7. }

输出:

start the computer! cpu startup! memory startup! disk startup! start computer finished! begin to close the computer! cpu shutdown! memory shutdown! disk shutdown! computer closed!

如果我们没有Computer类,那么,CPU、Memory、Disk他们之间将会相互持有实例,产生关系,这样会造成严重的依赖,修改一个类,可能会带来其他类的修改,这不是我们想要看到的,有了Computer类,他们之间的关系被放在了Computer类里,这样就起到了解耦的作用,这,就是外观模式!

10、桥接模式(Bridge)

Bridge模式将抽象部分与它的实现部分分离,使它 们都可以独立地变化。桥接的用意是:将抽象化与实现化解耦,使得二者可以独立变化,像我们常用的JDBC桥DriverManager一样,JDBC进行连接数据库的时候,在各个数据库之间进行切换,基本不需要动太多的代码,甚至丝毫不用动,原因就是JDBC提供统一接口,每个数据库提供各自的实现,用一个叫做数据库驱动的程序来桥接就行了。我们来看看关系图:

实现代码:

先定义接口:

  1. public interface Sourceable {
  2. public void method();
  3. }

分别定义两个实现类:

  1. public class SourceSub1 implements Sourceable {
  2. @Override
  3. public void method() {
  4. System.out.println("this is the first sub!");
  5. }
  6. }
  1. public class SourceSub2 implements Sourceable {
  2. @Override
  3. public void method() {
  4. System.out.println("this is the second sub!");
  5. }
  6. }

定义一个桥,持有Sourceable的一个实例:

  1. public abstract class Bridge {
  2. private Sourceable source;
  3. public void method(){
  4. source.method();
  5. }
  6. public Sourceable getSource() {
  7. return source;
  8. }
  9. public void setSource(Sourceable source) {
  10. this.source = source;
  11. }
  12. }
  1. public class MyBridge extends Bridge {
  2. public void method(){
  3. getSource().method();
  4. }
  5. }

测试类:

  1. public class BridgeTest {
  2. public static void main(String[] args) {
  3. Bridge bridge = new MyBridge();
  4. /*调用第一个对象*/
  5. Sourceable source1 = new SourceSub1();
  6. bridge.setSource(source1);
  7. bridge.method();
  8. /*调用第二个对象*/
  9. Sourceable source2 = new SourceSub2();
  10. bridge.setSource(source2);
  11. bridge.method();
  12. }
  13. }

output:

this is the first sub! this is the second sub!

这样,就通过对Bridge类的调用,实现了对接口Sourceable的实现类SourceSub1和SourceSub2的调用。接下来我再画个图,大家就应该明白了,因为这个图是我们JDBC连接的原理,有数据库学习基础的,一结合就都懂了。

11、组合模式(Composite)

Composite模式将对象组合成树形结构以表示‘部分- 整体’的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。组合模式有时又叫部分—整体模式在处理类似树形结构的问题时比较方便,看看关系图:

直接来看代码:

  1. public class TreeNode {
  2. private String name;
  3. private TreeNode parent;
  4. private Vector<TreeNode> children = new Vector<TreeNode>();
  5. public TreeNode(String name){
  6. this.name = name;
  7. }
  8. public String getName() {
  9. return name;
  10. }
  11. public void setName(String name) {
  12. this.name = name;
  13. }
  14. public TreeNode getParent() {
  15. return parent;
  16. }
  17. public void setParent(TreeNode parent) {
  18. this.parent = parent;
  19. }
  20. //添加孩子节点
  21. public void add(TreeNode node){
  22. children.add(node);
  23. }
  24. //删除孩子节点
  25. public void remove(TreeNode node){
  26. children.remove(node);
  27. }
  28. //取得孩子节点
  29. public Enumeration<TreeNode> getChildren(){
  30. return children.elements();
  31. }
  32. }
  1. public class Tree {
  2. TreeNode root = null;
  3. public Tree(String name) {
  4. root = new TreeNode(name);
  5. }
  6. public static void main(String[] args) {
  7. Tree tree = new Tree("A");
  8. TreeNode nodeB = new TreeNode("B");
  9. TreeNode nodeC = new TreeNode("C");
  10. nodeB.add(nodeC);
  11. tree.root.add(nodeB);
  12. System.out.println("build the tree finished!");
  13. }
  14. }

使用场景:将多个对象组合在一起进行操作,常用于表示树形结构中,例如二叉树,数等。

12、享元模式(Flyweight)

  运用共享技术有效地支持大量细粒度的对象,类图如下图:

  主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用。

FlyWeightFactory负责创建和管理享元单元,当一个客户端请求时,工厂需要检查当前对象池中是否有符合条件的对象,如果有,就返回已经存在的对象,如果没有,则创建一个新对象,FlyWeight是超类。一提到共享池,我们很容易联想到Java里面的JDBC连接池,想想每个连接的特点,我们不难总结出:适用于作共享的一些个对象,他们有一些共有的属性,就拿数据库连接池来说,url、driverClassName、username、password及dbname,这些属性对于每个连接来说都是一样的,所以就适合用享元模式来处理,建一个工厂类,将上述类似属性作为内部数据,其它的作为外部数据,在方法调用时,当做参数传进来,这样就节省了空间,减少了实例的数量。

看个例子:

看下数据库连接池的代码:

  1. public class ConnectionPool {
  2. private Vector<Connection> pool;
  3. /*公有属性*/
  4. private String url = "jdbc:mysql://localhost:3306/test";
  5. private String username = "root";
  6. private String password = "root";
  7. private String driverClassName = "com.mysql.jdbc.Driver";
  8. private int poolSize = 100;
  9. private static ConnectionPool instance = null;
  10. Connection conn = null;
  11. /*构造方法,做一些初始化工作*/
  12. private ConnectionPool() {
  13. pool = new Vector<Connection>(poolSize);
  14. for (int i = 0; i < poolSize; i++) {
  15. try {
  16. Class.forName(driverClassName);
  17. conn = DriverManager.getConnection(url, username, password);
  18. pool.add(conn);
  19. catch (ClassNotFoundException e) {
  20. e.printStackTrace();
  21. catch (SQLException e) {
  22. e.printStackTrace();
  23. }
  24. }
  25. }
  26. /* 返回连接到连接池 */
  27. public synchronized void release() {
  28. pool.add(conn);
  29. }
  30. /* 返回连接池中的一个数据库连接 */
  31. public synchronized Connection getConnection() {
  32. if (pool.size() > 0) {
  33. Connection conn = pool.get(0);
  34. pool.remove(conn);
  35. return conn;
  36. else {
  37. return null;
  38. }
  39. }
  40. }
 
通过连接池的管理,实现了数据库连接的共享,不需要每一次都重新创建连接,节省了数据库重新创建的开销,提升了系统的性能!

Design Mode 之 结构模式的更多相关文章

  1. [JAVA设计模式]第三部分:结构模式

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  2. c#桥接模式(bridge结构模式)

    桥接模式(bridge结构模式)c#简单例子 在前面的玩家中每增加一个行为,就必须在每个玩家中都增加,通过桥接模式将行为提取出来了,减少变化 ? 1 2 3 4 5 6 7 8 9 10 11 12 ...

  3. node(3)MVC代码结构模式moogoDB的学习

    ---恢复内容开始--- 一.MVC代码结构模式 设计模式:观察者模式.中介者模式,这种模式,主要做的事情是处理类与类之间‘高内聚.低耦合’; 代码架构模式:MVC.MVVM.MVP Model:模型 ...

  4. APP 自动化测试封装结构模式

    原文出处http://www.toutiao.com/a6268089772108333314/ 做过UI自动化测试同学,都会深深体会几个痛点:维护量大.适配量大.编写代码巨大等.基于这些问题,大家都 ...

  5. Flask - 特殊装饰器 和 Flask工作结构模式(FBV, CBV)

    目录 Flask - 特殊装饰器 和 Flask工作结构模式 @app.errorhandler() @app.before_request @app.after_request FBV和CBV Fl ...

  6. 十二、结构模式之门面(Facade)模式

    什么是门面模式 门面模式(也有翻译为外观模式)是对象的结构模式,外部与一个子系统的通信必须通过一个统一的门面进行.其为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子 ...

  7. 十一、结构模式之享元(Flyweight)模式

    什么是享元模式 享元模式是对象的结构模式,是运用共享技术来有效的支持大量细粒度的对象.享元对象能做到共享的关键是区分内蕴状态和外蕴状态.一个内蕴状态是存储在享元对象内部,并且是不会随环境改变而有所不同 ...

  8. 八、结构模式之组合(Composite)模式

    组合模式属于对象的结构模式,有时又叫做部分-整体模式,组合模式将对象组织到树结构中,可以用来描述整体与部分的联系.其可以使客户端将单纯元素和组合元素同等对待. 当需求中是体现部分与整体层次的结构时,以 ...

  9. 设计模式学习之路——Decorator装饰模式(结构模式)

    子类复子类,子类何其多 假如我们需要为游戏中开发一种坦克,除了各种不同型号的坦克外,我们还希望在不同场合中为其增加以下一种或多种功能:比如红外线夜视功能,比如水陆两栖功能,比如卫星定位功能等等. 动机 ...

随机推荐

  1. axis1调用方式

    axis http://10.15.22.28/itfmgr/services/ITaxManagement?wsdl package com.isoftstone.core.service.impl ...

  2. 给Adobe Reader添加书签功能

    Adobe Acrobat Professional和Adobe Reader都是Adobe公司的产品.前者用来编辑制作PDF文档,后者只能用来阅读PDF.令人郁闷的是Adobe Reader中虽然有 ...

  3. HDU 1392 Surround the Trees 构造凸包

    又是一道模板题 #include <iostream> #include <cstring> #include <cstdlib> #include <cst ...

  4. SpringMVC3.2+JPA使用注解的方式环境搭建

    ==============================entity=========================================================package ...

  5. Weka中EM算法详解

    private void EM_Init (Instances inst) throws Exception { int i, j, k; // 由于EM算法对初始值较敏感,故选择run k mean ...

  6. APP接口版本兼容的问题

    现在基本每个公司都做APP,所以大家都面临 APP接口版本兼容的问题. iOS和android 要不断开发新版本,很多服务端开发都是在以前接口的逻辑上进行修改.新的APP和接口开发后,接口如何兼容老的 ...

  7. css3 前缀

  8. 移动端轮播图插件(支持Zepto和jQuery)

    一. 效果图 二. 功能介绍 1. 支持图片自动轮播和非自动轮播 2. 支持点击和滑动. 三. 简单介绍 代码都有注释,逻辑简单,不做更多赘述. 1. 在你的html中添加一行. <sectio ...

  9. php 将字符串中的连续多个空格转换为一个空格

    转载自:http://www.phpernote.com/php-function/633.html /** * 多个连续空格只保留一个 * * @param string $string 待转换的字 ...

  10. 转载:as3.0下对象类型返回值与变量默认值的详细说明

    转自:http://www.cuplayer.com/player/PlayerCodeAs/2012/0905367.html Null.NaN.undefined 及各自应用对象:变量的默认值: ...