结构模式:
(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>