java必知必会-设计模式(中)


3.2. 结构型模式

3.2.1. 适配器模式

  • 适配器模式(Adapter Pattern)将一个类的接口转换成另一种接口.让原本接口不兼容的类可以兼容
  • 用户调用适配器转化处理的目标接口方法,适配器在调用被适配者的相关接口方法
  • C根据接口要求将不合适的类A转成接口要的类B,只不过传入不合适类A的方式不一样
public class Client {
    public static void main(String[] args) {
        new ClassAdapter().request();
        new ObjectAdapter(new Adaptee()).request();
    }
}

//目标接口
interface Target{public void request();}

//被适配类(不符合要求的类)
class Adaptee{public void specialRequest(){System.out.println("适配者中的业务代码被调用!");}}


//类适配器模式-适配器类实现目标接口并继承被适配类完成目标接口转换
class ClassAdapter extends Adaptee implements Target{
    @Override
    public void request(){
        specialRequest();
    };
}

//对象适配模式-对象适配类
class ObjectAdapter implements Target{
    private Adaptee adaptee;//继承改成实现 关联关系-聚合
    public ObjectAdapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }
    @Override
    public void request(){
        adaptee.specialRequest();
    }
}

//接口适配器模式-接口适配类
abstract class InterfaceAdapter implements Target{public void request(){}}

public class Client {
    public static void main(String[] args) {
        InterfaceAdapter interfaceAdapter = new InterfaceAdapter() {
            @Override
            public void request() {
                System.out.println("使用了m1的方法");
            }
        };
        interfaceAdapter.request();
    }
}

3.2.2. 桥接模式

  • 桥接模式(Bridge模式):将实现与抽象放在两个不同的类层次中,使两个层次可以独立改变。
  • 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围有一定的局限性,即需要有这样的应用场景。
  • 对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用.
  • 常见的应用场景: -JDBC驱动程序
  • 银行转账系统
    • 转账分类: 网上转账,柜台转账,AMT转账
    • 转账用户类型:普通用户,银卡用户,金卡用户…
  • 消息管理
    • 消息类型:即时消息,延时消息
    • 消息分类:手机短信,邮件消息,QQ消息…
//桥接模式:将C的功能抽象为接口A,将A的功能桥接回到C上,调用C的方法相当于调用A的实现方法
public class Client{
    public static void main(String[] args) {
        RefinedAbstraction refinedAbstraction = new RefinedAbstraction();
        refinedAbstraction.setImplementor(new ConcreteImplementorA());
        refinedAbstraction.do1();
    }
}

//实现化角色
interface Implementor{public void do1();}

//具体实现化角色
class ConcreteImplementorA implements Implementor{public void do1(){}}

//抽象化角色
abstract class Abstraction {//桥接类
    protected Implementor implementor;
    public void setImplementor(Implementor implementor){this.implementor = implementor;}
    protected abstract void do1();
}

//扩展抽象化角色
class RefinedAbstraction extends Abstraction{
    public void do1(){this.implementor.do1();System.out.println("子类自己的功能");}
}

3.2.3. 装饰者模式

  • 装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性
public class Client {
    public static void main(String[] args) {
        D d = new D();
        d.setA(new B());
        System.out.println(d.fun());
    }
}

//抽象构件角色
interface A{int fun();}

//具体构件角色
class B implements A{public int fun(){return 1;}}

//抽象装饰角色
abstract class C implements A{
    private A a;
    public void setA(A a){this.a = a;}
    public int fun(){return a.fun();};
}

//具体装饰角色
class D extends C{
    public int fun(){
        return super.fun() + decorate();
    }
    private int decorate(){System.out.println("为具体构件角色增加额外的功能");return 1;}//装饰
}

3.2.4. 组合模式

  • 组合模式(Composite Pattern),又叫部分整体模式,它创建了对象组的树形结构,将对象组合成树状结构以表示“整体-部分”的层次关系
  • 组合模式让客户以一致的方式处理个别对象以及组合对象
public class Client{
    public static void main(String[] args) {
        B b = new B();
        C c = new C();
        D d = new D();
        b.addA(c);
        c.addA(d);
        b.print();
    }
}

//BCD实现继承一个抽象类达到嵌套组合的目的,层层包含的关系
abstract class A{protected void addA(A a){} protected abstract void print();}

class B extends A{
    private List<A> list = new ArrayList<A>();
    public void addA(A a){list.add(a);}
    public void print(){for(A a:list){a.print();}}
}

class C extends A{
    private List<A> list = new ArrayList<A>();
    public void addA(A a){list.add(a);}
    public void print(){for(A a:list){a.print();}}
}

class D extends A{
    private List<A> list = new ArrayList<A>();
    public void addA(A a){list.add(a);}
    public void print(){System.out.print("D");};
}

3.2.5. 外观者模式

  • 外观模式(Facade)为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用
  • 外观模式通过定义一个一致的接口,用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节
  • 当系统需要分层次设计时,可以使用
//对Facade操作实现对A和B操作
public class Client{
    public static void main(String[] args) {
        Facade f = new Facade();
        f.setA(new A());
        f.setB(new B());
        f.fun();
        f.fun2();
    }
}

class Facade{
    private A a;
    private B b;
    public void setA(A a){this.a=a;}
    public void setB(B b){this.b=b;}
    public void fun(){
        a.fun1();b.fun4();//分两个层次了
    }
    public void fun2(){
        b.fun3();a.fun2();
    }
}

class A {public void fun1(){System.out.println("fun1");} public void fun2(){System.out.println("fun2");}}
class B {public void fun3(){System.out.println("fun3");} public void fun4(){System.out.println("fun4");}}

3.2.6. 享元模式(池技术)

  • 享元模式(Flyweight Pattern): 运用共享技术有效地支持大量细粒度的对象,减少对象的创建
  • 常用于系统底层开发,解决系统的性能问题。像数据库连接池,里面都是创建好的连接对象,在这些连接对象中有我们需要的则直接拿来用,避免重新创建,如果没有我们需要的,则创建一个
  • 享元模式能够解决重复对象的内存浪费的问题,当系统中有大量相似对象,需要缓冲池时。不需总是创建新对象,可以从缓冲池里拿。这样可以降低系统内存,同时提高效率
  • 享元模式经典的应用场景就是池技术了,String常量池、数据库连接池、缓冲池等等都是享元模式的应用,享元模式是池技术的重要实现方式
public class Client{
    public static void main(String[] args) {
        B b = new B();
        ((A) b.getA("test")).fun();
        ((A) b.getA("test")).fun();
    }
}
class A{public void fun(){System.out.println(this);}}

class B{
    private Map<String,A> map = new HashMap<String,A>();
    public A getA(String name){
        A a = map.get(name);
        if(a == null){
            A nA = new A();
            map.put(name,nA);
            return nA;
        }
        return a;
    }
}

3.2.7. 代理模式(JDK与cglib)

  • 代理模式:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
  • 被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象
  • 代理模式有不同的形式, 主要有三种 静态代理、动态代理 (JDK代理、接口代理)和 Cglib代理 (可以在内存动态的创建对象,而不需要实现接口, 他是属于动态代理的范畴) 。
  • 代理模式(Proxy)的变体
    • 防火墙代理:内网通过代理穿透防火墙,实现对公网的访问。
    • 缓存代理:比如:当请求图片文件等资源时,先到缓存代理取,如果取到资源则ok,如果取不到资源,再到公网或者数据库取,然后缓存。
    • 远程代理:远程对象的本地代表,通过它可以把远程对象当本地对象来调用。远程代理通过网络和真正的远程对象沟通信息
    • 同步代理:主要使用在多线程编程中,完成多线程间同步工作
- 静态代理:需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类;一旦接口增加方法,目标对象和代理对象都要维护,因为代理对象和目标对象实现一样的接口,所以会有很多代理类
public class Client{
    public static void main(String[] args) {
        B b = new B();
        C c = new C();
        c.setA(b);
        c.fun();
    }
}
interface A{void fun();}

class B implements A{public void fun(){System.out.println("目标对象...");}}//被代理对象/目标对象

class C implements A{//代理对象
    private B b;
    private A a;
    public void setA(A a){this.a = a;}
    public void fun(){
        System.out.println("前处理....");
        this.a.fun();
        System.out.println("后处理....");
    }
}

动态代理模式的(JDK代理、接口代理)
- 代理对象不需要实现接口,但是目标对象要实现接口
- 代理类所在包:java.lang.reflect.Proxy
- JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是: 
    - static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
    - ClassLoader 指定当前目标对象使用的类加载器,获取加载器的方法固定
    - Class<?>[] interfaces 目标对象实现的接口类型,使用泛型方法确认类型
    - InvocationHandler h 执行目标对象的方法时,会触发该方法,会把当前执行的对象方法当参数传入

public class Client {
    public static void main(String[] args) {
        A proxyInstance = (A)new ProxyFactory(new B()).getProxyInstance();
        proxyInstance.a();
    }
}

interface A {void a();}

class B implements A{ public void a() {System.out.println("代理对象");} }//被代理对象/目标对象

class ProxyFactory {
    private Object target;
    public ProxyFactory(Object target) {this.target = target;}
    public Object getProxyInstance() {//代理对象
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
                new InvocationHandler() {
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("前处理....");
                        Object object = method.invoke(target, args);
                        System.out.println("后处理....");
                        return object;
                    }
                });
    }
}

Cglib代理/子类代理
- Cglib代理是在内存中构建一个子类对象从而实现对目标对象功能扩展。
- Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接 口.它广泛的被许多AOP的框架使用,例如Spring AOP,实现方法拦截
- 在AOP编程中如何选择代理模式:目标对象需要实现接口,用JDK代理、目标对象不需要实现接口,用Cglib代理
- Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类
public class Client {
    public static void main(String[] args) {
        A target = new A();
        ((A)new ProxyFactory(target).getProxyInstance()).fun();
    }
}

class A{public void fun(){System.out.println("代理对象...");}}

class ProxyFactory implements MethodInterceptor {
    private Object target;
    public ProxyFactory(Object target) {this.target = target;}
    public Object getProxyInstance() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }
    @Override
    public Object intercept(Object arg0, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("前处理....");
        Object object = method.invoke(target, args);
        System.out.println("后处理....");
        return object;
    }
}

总结

本文介绍了的设计模式使用,如有问题欢迎私信和评论

  • 23
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程岁月

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值