设计模式-结构模式
来源:互联网 发布:linux zip文件压缩 编辑:程序博客网 时间:2024/05/27 19:25
结构模式:
(1)Proxy-代理模式
定义:为一个对象提供代理,以控制对象的访问.
优点:
1. 代理模式能够协调调用者和被调用者,在一定程度上降低了系统的耦合度。
2. 代理对象将被代理对象透明化.
3. 具有较高的扩展性.被代理对象的修改不会影响代理对象及其外部调用.
缺点:
1. 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
2. 实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
应用场景:
1. 远程代理:为一个对象在不同的地址空间提供局部代表。这样可以隐藏一个对象存在于不同地址空间的事实。
2. 虚拟代理:通过使用过一个小的对象代理一个大对象。这样就可以减少系统的开销。
3. 保护代理:用来控制对真实对象的访问权限。
代理的好处:
1. 可以在间接访问对象的同时,要其前或后,添加其它的逻辑代码.
2. 对原来逻辑进行添加其它逻辑,最终生成新的逻辑.即:对类的方法添加一些额外的逻辑,生成新的方法逻辑
// 有一个Moveable接口public interface Moveable { public void move();}// 有一辆Tank实现了它public class Tank implements Moveable{ public void move() { System.out.println("Tank moving ..."); try { Thread.sleep(new Random().nextInt(5000)); } catch (InterruptedException e) { e.printStackTrace(); } }}// 现在想知道这辆Tank移动了多长时间,即move方法运行的时间// 方法一:可以直接在 System.out.println("Tank moving ..."); 前后加上Long start/stop = System.currentTimeMillis();直接更改源码,但不灵活// 方法二:使用继承实现TimeTank代理类,在子类中覆盖Tank的move方法,Java中没有多继承,继承了Tank就不能继承其他类了,不推荐public class TimeTank1 extends Tank{ public void move(){ before(); super.move(); after(); } public Long before(){ return System.currentTimeMillis(); } public Long after(){ return System.currentTimeMillis(); }}// 方法三:使用静态代理TimeTank,在Tank调用move前后加上响应的逻辑,组合 推荐,灵活public class TankTimeProxy implements Moveable{ Moveable t; public TankTimeProxy(Moveable t){ this.t = t; } public Long before(){ return System.currentTimeMillis(); } public Long after(){ return System.currentTimeMillis(); } public void move() { Long start = before(); t.move(); Long stop = after(); System.out.println("Tank 运行了 "+((stop-start)/1000)+"s 时间"); }}/** -----延伸:-----如果想知道Tank的运行日志,可以日志代理类LogTank,在Tank调用move前后加上响应的逻辑,组合 推荐,灵活 */public class TankLogProxy implements Moveable{ Moveable t; public TankLogProxy(Moveable t){ this.t = t; } public void move() { System.out.println("Tank start move..."); t.move(); System.out.println("Tank stop move..."); }}// 测试:先记录日志,再计算运行时间public class Client { public static void main(String[] args) { Tank t = new Tank(); Moveable timeTank = new TankTimeProxy(t); Moveable logTank = new TankLogProxy(timeTank); logTank.move(); }}
(2)动态代理
Java 动态代理类位于java.lang.reflect包下,一般涉及到以下两个类:
1. interface InvocationHandler 该接口仅定义了一个方法
invoke(Object obj, Method method, Object[] args)
其中,obj 是指代理类 , method 被代理的方法, args 为该方法的参数数组 ,这个抽象方法在代理类中动态实现
2. Proxy 该类即为动态代理类,主要包括
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHander h); 返回代理类的一个实例
所谓Dynamic Proxy是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些 interface。
在使用动态代理类时,我们必须实现InvocationHandler接口
如Tank的时间、日志代理用动态代理的实现代码:
// 方法调用前后的时间处理public class TimeHandler implements InvocationHandler{ private Object target; public TimeHandler(Object target) { super(); this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Long start = before(); method.invoke(target, new Object[]{}); Long stop = after(); System.out.println("Tank 运行了 "+((stop-start)/1000)+"s 时间"); return null; } public Long before(){ return System.currentTimeMillis(); } public Long after(){ return System.currentTimeMillis(); }}// 方法调用前后的日志处理public class LogHandler implements InvocationHandler{ private Object target; public LogHandler(Object target) { super(); this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("日志--方法执行前"); method.invoke(target, new Object[]{}); System.out.println("日志--方法执行后"); return null; }}// 测试时间:public static void main(String[] args) { Tank t = new Tank(); InvocationHandler h = new TimeHandler(t); Moveable m = (Moveable) Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(), h); m.move();}输出结果:Tank moving ...Tank 运行了 2s 时间// 测试时间和日志:public static void main(String[] args) { Tank t = new Tank(); InvocationHandler h = new TimeHandler(t); Moveable m = (Moveable) Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(), h); InvocationHandler h1 = new LogHandler(m); Moveable m1 = (Moveable) Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(), h1); m1.move(); }执行结果:日志--方法执行前Tank moving ...Tank 运行了 4s 时间日志--方法执行后
(3)Facade-外观(门面)模式
定义:为子系统的一组接口提供统一的接口,使得子系统更容易使用.将复杂的过程包含在里面,提供一个简单的应用接口即可
优点:
1. 减少了系统的依赖.系统只依赖于被门面模式封装好的高级接口,而不依赖于子系统的内部接口.
2. 提高灵活性.子系统内部的改变,不会对门面对象产生影响.
3. 提高安全性.将子系统内部的实现透明化.
缺点:
不符合开闭原则.对于门面对象,若要修改,则需要重新写.
应用场景:
1. 当要为访问一系列复杂的子系统提供一个简单入口时。facade类很好的屏蔽了子系统中因不断演化而产生的越来越多的小类,降低访问子系统的复杂性。
2. 客户端程序与多个子系统之间存在很大的依赖性。引入外观类可以将子系统与客户端解耦,提高子系统的独立性和可移植性。
3. 当需要构建一个层次结构的子系统时,使用facade模式定义子系统中每层的入口点。如果子系统之间是相互依赖的,则可以让它们仅通过facade进行通讯,从而简化子系统间的依赖关系。
public class Facade { PutElephantIntoBridge putElephantIntoBridge = new PutElephantIntoBridge(); public void option(){ putElephantIntoBridge.openBraidgeGate(); putElephantIntoBridge.putElephantIntoBridge(); putElephantIntoBridge.closeBraidgeGate(); } public static void main(String[] args){ Facade facade = new Facade(); facade.option(); }}class PutElephantIntoBridge{ public void openBraidgeGate(){ System.out.println("冰箱门已打开."); } public void putElephantIntoBridge(){ System.out.println("把大象放入冰箱."); } public void closeBraidgeGate(){ System.out.println("冰箱门已关闭."); }}
(4)Adaptor-适配器模式
定义:将一个接口转变成客户所期望的接口,从而使本来因为接口不匹配,不能在一起工作的两个接口,可以在一起工作.
将一个已存在的类/接口进行复用,将其转换/具体化成客户希望的另外的一个类/接口
优点:
1. 可以让两个完全没有关系的接口可以一起工作(当然前提是需要适配器来进行处理).
2. 增加了接口的透明性.具体的实现是封装在适配者中,对于客户来说是透明的.
3. 提高了接口的复用性.
4. 增加了灵活性.修改适配器,而不会对系统产生影响.
缺点:
对于Java、C#等不支持多重继承的语言,一次最多只能适配一个适配者类,而且目标抽象类只能为接口,不能为类,其使用有一定的局限性,不能将
一个适配者类和他的子类同时适配到目标接口.
应用场景:
1. 系统需要使用现有的类,而这些类的接口不符合系统的需要.
2. 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类.
实现Adapter方式,其实”think in Java”的”类再生”一节中已经提到,有两种方式:组合(composition)和继承(inheritance).
适配器并不是通过继承来获取适配类(原)的功能的,而是通过适配类的对象来获取的,这就解决了java不能多继承所带来的不便了。
这也是java提倡的编程思想之一,即尽量使用聚合不要使用继承。
// 客户端期望的功能:求两个数的加减法public interface Operation{ public int sum(int a, int b); public int minus(int a, int b);}// 现有的实现有public class Sum{ public int sum(int a, int b){return a+b;}}public class Minus{ public int minus(int a, int b){return b-b;}}// 用对象适配器来完成我们需要的功能public class AdaptorOperation implements Operation{ private Sum sum; private Minus minus; public int sum(int a, int b){ return sum.sum(a,b);} public int minus(int a, int b){ return minus.minus(a,b);}}
(5)Composite-组合模式
定义:将对象组合成树形结构,以表示"部分与整体"的关系,使得用户在对待单个对象和组合对象时具有一致性.
优点:
1. 组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,它让客户端忽略了层次的差异,方便对整个层次结构进行控制。
2. 客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码。
3. 在组合模式中增加新的容器构件和叶子构件都很方便,无须对现有类库进行任何修改,符合“开闭原则”。
4. 组合模式为树形结构的面向对象实现提供了一种灵活的解决方案,通过叶子对象和容器对象的递归组合,可以形成复杂的树形结构,但对树形结构的控制却非常简单。
缺点:
1. 在增加新构件时很难对容器中的构件类型进行限制。有时候我们希望一个容器中只能有某些特定类型的对象,例如在某个文件夹中只能包含文本文件
2. 使用组合模式时,不能依赖类型系统来施加这些约束,因为它们都来自于相同的抽象层,在这种情况下,必须通过在运行时进行类型检查来实现,这个实现过程较为复杂
应用场景:
1. 需要使用到部分-整体关系的场景.
2. 从整体能够独立出部分的场景.
</pre></div><h3>(6)Decorator-装饰者模式 </h3><p><strong>定义:</strong>动态地为对象添加额外职责,比继承更加灵活.</p><p><strong>优点:</strong></p><div><p> 1. 装饰者与被装饰者相互独立,不会耦合. </p><p> 2. 良好的扩展性.</p><p> 3. 是继承的代替.</p></div><p><strong>缺点:</strong></p><div><p>多层装饰者是很复杂的.</p></div><p><strong>应用场景:</strong></p><div><p> 1. 需要扩展一个类的功能,或者增加附加功能.</p><p> 2. 需要动态地给一个对象增加功能,这些功能还需要动态地撤销.</p><p> 3. 需要对一批类进行改装或者增加附加功能. </p></div><p>通过采用组合、而非继承的手法,Decorator模式实现了在运行时动态地扩展对象功能的能力,而且可以根据需要扩展多个功能。 避免了单独使用继承带来的“灵活性差”和”多子类衍生问题”</p><p>比如工厂生产杯子容量有 300ml、500ml ,但是又要求给杯子加颜色,有red红色、green绿色,可以生产红色300ml的,也可以生产绿色300ml的</p><div><pre name="code" class="java">// 原有生产杯子的,只需定义颜色的对象,通过组合扩展原有生产类的功能:public abstract class Cup { abstract void volume();}public class Cup300ml extends Cup{ //在类里面,添加组合接口类Color private Color color; //在构造器中,将组合成员以参数形式传入 public Cup300ml(Color color) { super(); this.color = color; } @Override void volume() { System.out.print("--300ml--"); color.color(); }}public class Cup500ml extends Cup{ private Color color; public Cup500ml(Color color) { super(); this.color = color; } @Override void volume() { System.out.print("--500ml--"); color.color(); }}public interface Color { public void color();}public class ColorGreen implements Color{ public void color() { System.out.println("绿色green--"); }}public class ColorRed implements Color{ public void color() { System.out.println("红色red--"); }}// 测试:public class Client { public static void main(String[] args){ Color red = new ColorRed(); Color green = new ColorGreen(); //生产一个300ml红色的cup Cup cup300mlRed = new Cup300ml(red); cup300mlRed.volume(); //生产一个300ml绿色的cup Cup cup300mlGreen = new Cup300ml(green); cup300mlGreen.volume(); //生产一个500ml的cup Cup cup500ml = new Cup500ml(red); cup500ml.volume(); }}
(7) Flyweight-享元模式
定义:使用共享对象可以有效地支持大量的细粒度的对象.就是说在一个系统中如果有多个相同的对象,那么只共享一份就可以了,不必每个都去实例化一个对象。
优点:
大大减少了应用程序中创建的对象的数量,降低了内存开销.
缺点:
1. 提高了系统的复杂度.需要分离出外部状态和内部状态.
2. 享元对象的状态外部化,增加了读取状态的时间.
应用场景:
1. 系统中存在大量的相似对象.
2. 细粒度的对象可以分离出外部状态和内部状态.
3. 需要缓冲池的场景.
Flyweight(享元)模式中常出现Factory模式。Flyweight的内部状态是用来共享的,Flyweight factory负责维护一个对象存储池(Flyweight Pool)
来存放内部状态的对象。
Flyweight的关键思路,在于新建对象时:
先到hashtable中进行获取–>判断取得对象是否为空–>若是,则新建此对象,且放回hashtable –>若存在,则共享原来的对象
Integer 类的源码中就使用了对象缓存池,具体可看源码
public interface Car { public void showCarName();}public class BmwCar implements Car{ private String name; public BmwCar(){ name = "Bmw"; } public void showCarName() { System.out.println("The BmwCar coming..."); }}public class BenzCar implements Car{ private String name; public BenzCar(){ name = "Benz"; } public void showCarName() { System.out.println("The BenzCar coming..."); }}public class CarFactory { public static Car getCar(String name){ Car car = null; if(name.equalsIgnoreCase("Bmw")){ car = new BmwCar(); }else if(name.equalsIgnoreCase("Benz")){ car = new BenzCar(); }else{} return car; }}public class CarFlyWeightFactory { public Car car; private Hashtable<string car=""> carPool = new Hashtable<string car="">(); public Car getCar(String name){ if(name.equalsIgnoreCase("Bmw")){ car = carPool.get(name); if(car == null){ car = new BmwCar(); carPool.put("Bmw", car); } }else if(name.equalsIgnoreCase("Benz")){ car = carPool.get(name); if(car == null){ car = new BenzCar(); carPool.put("Benz", car); } }else{} return car; } public int getNumber(){ return carPool.size(); }}// 测试:public class Client { public static void main(String[] args) { CarFlyWeightFactory carFlyWeightFactory = new CarFlyWeightFactory(); Car car = carFlyWeightFactory.getCar("Bmw"); car.showCarName(); Car car1 = carFlyWeightFactory.getCar("Bmw"); car1.showCarName(); if(car1 == car){ System.out.println("同一部车来的"); }else{ System.out.println("不同一部车来的"); } System.out.println("车的数量是:"+carFlyWeightFactory.getNumber()); }}输出结果:The BmwCar coming...The BmwCar coming...同一部车来的车的数量是:1</string></string>
(8) Bridge-
- 设计模式-结构模式
- 设计模式之结构模式
- 设计模式之结构模式
- 设计模式之结构模式
- 《设计模式》结构型模式
- 设计模式:结构型模式
- 设计模式:结构型模式
- 设计模式--结构型模式
- 设计模式--结构型模式
- java设计模式--结构模式
- 设计模式 -- 结构型模式
- 设计模式----结构型模式
- 设计模式---结构型
- 结构型设计模式
- 结构型设计模式
- 设计模式-结构型
- 设计模式-----结构型
- 结构型设计模式
- 搭建spring开发环境
- 《iOS Human Interface Guidelines》——Text View
- 蓝桥杯 翻硬币
- Android ViewPager使用详解
- 【杭电oj】2050 - 折线分割平面(递推)
- 设计模式-结构模式
- 125. Valid Palindrome
- github客户端怎样上传代码 mac
- JavaWeb开发之十二:JSP九大内置对象
- [容斥] LightOJ 1117 - Helping Cicada
- 杭电2008
- Java中的异常
- android蓝牙开发 蓝牙设备的查找和连接
- 【leetcode】 39. Combination Sum