精华内容
下载资源
问答
  • 对象结构型模式

    万次阅读 2019-11-27 21:55:46
    对象结构型模式 结构型模式(Structural Pattern)描述如何将类或者对象结合在一起形成更大的结构 适配器模式 在适配器模式中可以定义一个包装类,包装不兼容接口的对象,这个包装类指的就是适配器(Adapter),它所...

    对象结构型模式

    结构型模式(Structural Pattern)描述如何将类或者对象结合在一起形成更大的结构

    适配器模式

    在适配器模式中可以定义一个包装类,包装不兼容接口的对象,这个包装类指的就是适配器(Adapter),它所包装的对象就是适配者 (Adaptee),即被适配的类。适配器提供客户类需要的接口,适配器的实现就是把客户类的请求 转化为对适配者的相应接口的调用。适配器可以使由于接口不兼容而不能交互的类可以一起工作。这就是适配器模式的模式动机。

    简单的说,需要实现一个接口,这个接口的功能已经有一个类实现,但这个类却不符合接口的规则,因此使用适配器Adapter包装适配者Adaptee(可以有多个Adaptee),调用Adaptee的方法进行二次封装到Adapter的方法中,使用者调用Adapter的方法,并不关心适配器如何实现,反正Adapter对外能够提供功能即可

    模式定义 :适配器模式(Adapter Pattern) :将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作。

    适配器的核心在于使用新的类实现旧接口,老接口不能轻易动,新的实现类不符合老接口的规则,因此使用适配器实现老接口,调用新类的方法,相当于对各种实现类进行统合,对外能够通过统一的旧接口提供功能。

    img

    Outlet提供电压,AC plug插不进去,使用Adapter进行中间转化,使用者可以利用多态使用AC plug返回一个Adapter,Adapter调用Outlet提供电压。

    M3LtmV.md.png

    适配器模式有三种实现方式:

    类适配器

    Adapter继承功能类实现老接口,通过super调用方法

    @Configuration
    public class SpringConfiguration {
        @Bean
        public Outlet220V outlet220V(){
            return new Outlet220V();
        }
    
        @Bean(name="adapterA")
        public AdapterA adapterA(){
            return new AdapterA();
        }
    }
    public interface Outlet5V {
        //提供5V电压
        int outlet5V();
    }
    //新的实现类,能提供220V电压
    public class Outlet220V {
        public int outlet220V(){
            System.out.println("我被适配器调用,我能输出220V电压");
            return 220;
        }
    }
    //类适配器
    public class AdapterA extends Outlet220V implements Outlet5V {
        @Override
        public int outlet5V(){
            System.out.println("我是类适配器,继承实现类,实现老接口");
            return super.outlet220V()/44;
        }
    }
    
    public class AdapterPatternTest {
        public static void main(String[] args){
            ApplicationContext context= new AnnotationConfigApplicationContext(SpringConfiguration.class);
    
            Outlet5V outlet5V=(AdapterA)context.getBean("adapterA",AdapterA.class);
            outlet5V.outlet5V();
        }
    }
    

    对象适配器

    内部维护实现对象,调用实现方法实现老接口:

    public class AdapterB implements Outlet5V{
    
        private Outlet220V outlet220V=new Outlet220V();
    
        public int outlet5V(){
            System.out.println("我是对象适配器,内部维护功能对象,调用实现方法实现老接口");
            return outlet220V.outlet220V()/44;
        }
    }
    

    接口适配

    介绍完类适配器和对象适配器,我们再来看看接口适配器,接口适配器相对类适配器和对象适配器而言,接口适配器相对更加灵活,就好比手机适配器中的万能适配器,不管接入的是多少伏的电源,最终都能保证输出电源为5V。

    首先,定义一个总的抽象类,并且给予一个默认值(或者直接是接口,目的都是为了定义功能),即提供总的抽象

    public abstract class AbstractAdapter {
        public int output(){
            return 220;
        }
    }
    

    基于该抽象类重写方法,提供不同的功能

    public class Outlet110V extends AbstractAdapter {
        @Override
        public int output(){
            return 110;
        }
    }
    public class Outlet440V extends AbstractAdapter {
        @Override
        public int output(){
            return 440;
        }
    }
    

    适配器内部维护一个总的抽象对象,提供不同的构造器,提供setter,实现老接口时,必须能判断当前维护的抽象对应的具体实现是哪一个,可使用instanceof或者反射

    public class AdapterC implements Outlet5V {
        private AbstractAdapter abstractAdapter;
        public AdapterC(AbstractAdapter abstractAdapter){
            this.abstractAdapter=abstractAdapter;
        }
    
        //这里是接口适配器,内部维护总的抽象,可以是接口,也可以是抽象类,对外提供方法,因此必须有办法判断
        //具体是哪一个实现,可使用instanceof,也可以使用反射读取信息
        @Override
        public int outlet5V(){
            System.out.println("我是接口适配器,内部维护一个总抽象,根据instanceof判断当前是哪一个实现");
            if(abstractAdapter instanceof  Outlet110V)return abstractAdapter.output()/22;
            else if(abstractAdapter instanceof Outlet440V)return abstractAdapter.output()/88;
            return 5;
        }
    }
    

    桥接模式

    将抽象部分与它的实现部分分离,使它们都可以独立地变化。将抽象化(Abstraction)与实现化 (Implementation)脱耦,使得二者可以独立地变化。

    假设有两个接口A与B,A有6个具体实现,B有4个具体实现,现在必须组合A,B以提供组合式的更强的功能,共有6*4=24个具体提供,为实现解耦,现在将某一个接口抽象化,将A变为抽象类,内部维护一个B并提供setter,A的具体实现都要继承A,因为A内部维护一个B并提供setter,因此A不关心具体的B是哪一个,调用功能即可,只需6+4=10个具体提供。

    模式结构 :

    • Abstraction:抽象类,内部维护一个Implementor并提供setter

    • RefinedAbstraction:扩充抽象类

    • Implementor:实现类接口

    • ConcreteImplementor:具体实现类

    McFXVO.md.png

    抽象化角色和实现化角色可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将一个抽象化子类的对象和一个实现化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合。

    • 一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。

    • 对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增 加的系统,桥接模式尤为适用。

    //实现接口与具体实现
    public interface VideoStream {
        public void videoStream();
    }
    public class AVIVideoStream implements VideoStream {
        @Override
        public void videoStream(){
            System.out.println("提供AVI视频流");
        }
    }
    public class MP4VideoStream implements VideoStream {
        @Override
        public void videoStream(){
            System.out.println("提供mp4视频流");
        }
    }
    
    //抽象类与扩充实现
    public abstract class VideoPlay {
        private VideoStream videoStream;
        public VideoPlay(VideoStream stream){
            this.videoStream=stream;
        }
        public VideoStream getVideoStream(){
            return videoStream;
        }
    
        public void play(){
        }
    }
    public class Aplay extends VideoPlay {
        public Aplay(VideoStream stream){
            super(stream);
        }
        @Override
        public void play(){
            System.out.println("获取视频流");
            super.getVideoStream().videoStream();
            System.out.println("A play");
        }
    }
    public class Bplay extends VideoPlay {
        public Bplay(VideoStream videoStream){
            super(videoStream);
        }
    
        @Override
        public void play(){
            System.out.println("获取视频流");
            super.getVideoStream().videoStream();
            System.out.println("B play");
        }
    }
    
    public class BridagePatternTest  {
        public static void main(String[] args){
            VideoPlay play=new Aplay(new AVIVideoStream());
            play.play();
    
            play=new Aplay(new MP4VideoStream());
            play.play();
    
            play=new Bplay(new AVIVideoStream());
            play.play();
    
            play=new Bplay(new MP4VideoStream());
            play.play();
        }
    }
    /*
    获取视频流
    提供AVI视频流
    A play
    获取视频流
    提供mp4视频流
    A play
    获取视频流
    提供AVI视频流
    B play
    获取视频流
    提供mp4视频流
    B play
    */
    

    组合模式

    组合模式描述了如何将容器对象和叶子对象进行递归组合,使得用户在使用时无须对它们进行区分,可以一致地对待容器对象和叶子对象,组合多个对象形成树形结构以表示“整体-部分”的结构层次。

    模式结构:

    • Component: 抽象构件

    • Leaf: 叶子构件

    • Composite: 容器构件

    • Client: 客户类

    也就是整体与部分的模式,极其类似于文件与文件夹,文件夹里面可以有子文件夹,也可以有文件,对于这样的抽象树形结构,定义一个抽象构件Component,客户端调用抽象构件,这个抽象构件可以是文件也可以是文件夹,如果是文件(Leaf)直接执行操作,如果是文件夹(Composite)则调用内部维护的抽象构件

    Component列表递归遍历。

    MfOxMV.md.png

    即定义一个公共抽象类,该公共抽象的子类可以是具体实现,也可以是抽象容器,是容器的话内部就需要维护抽象类的集合列表以供遍历

    //公共抽象Component
    public abstract class File {
        public abstract void read();
        public abstract void write();
    
        public abstract void add(File file);
        public abstract void remove(File file);
    }
    
    //具体实现
    public class TextFile extends File {
    
        @Override
        public void read(){
            System.out.println("读文本文件");
        }
        @Override
        public void write(){
            System.out.println("写文本文件");
        }
    
        @Override
        public  void add(File file){
    
        }
        @Override
        public void remove(File file){
    
        }
    }
    
    public class VideoFile extends File{
        @Override
        public void read(){
            System.out.println("读视频文件");
        }
        @Override
        public void write(){
            System.out.println("写视频文件");
        }
    
        @Override
        public  void add(File file){
    
        }
        @Override
        public void remove(File file){
    
        }
    }
    
    //抽象容器,可继续递归
    public class Folder extends File {
    
        //维护文件列表
        private List<File> fileList=new ArrayList<>();
    
        @Override
        public void read(){
        }
        @Override
        public void write(){
        }
    
        @Override
        public  void add(File file){
            fileList.add(file);
        }
        @Override
        public void remove(File file){
            fileList.remove(file);
        }
    
        public List<File> getFileList(){
            return fileList;
        }
    }
    
    public class CompositePatternTest {
    
        public static void main(String[] args){
            Folder folder=new Folder();
            Folder subfolder=new Folder();
            subfolder.add(new TextFile());
    
            folder.add(new VideoFile());
            folder.add(subfolder);
    
            dfs(folder);
        }
    
        public static void dfs(File file){
            if(file instanceof Folder){
                for(File file1:((Folder) file).getFileList()){
                    dfs(file1);
                }
            }
            file.read();
        }
    }
    

    组合模式非常适用于可递归描述的树形数据结构,如目录解析,XML解析

    可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,使得增加新构件也更容易。 定义了包含叶子对象和容器对象的类层次结构,叶子对象可以被组合成更复杂的容器对象,而这个容器对象又可以被组合,这样不断递归下去,可以形成复杂的树形结构。缺点就是变得更加抽象,很难对新增构件的类型进行限制。

    装饰模式

    一般有两种方式可以实现给一个类或对象增加行为:

    • 继承机制,使用继承机制是给现有类添加功能的一种有效途径, 通过继承一个现有类可以使得子类在拥有自身方法的同时还拥有父类的方法。但是这种方法是静态的,用户不能控制增加行为的方式和时机。

    • 关联机制,即将一个类的对象嵌入另一个对象中,由另一个对象来决定是否调用嵌入对象的行为以便扩展自己的行为,我们称这个嵌入的对象为装饰器(Decorator)。

    装饰模式可以在不需要创造更多子类的情况下,将对象的功能加以扩展。装饰者内部维护原始对象,通过构造方法或者setter进行注入,对原始对象的方法进行重写改造,提供增强后的功能,即成为包装器Wrapper。客户端通过调用装饰器以获得原始对象增强后的功能,Java.io中的BufferInputStream等缓冲流是典型的装饰器模式。

    模式结构:

    • Component: 抽象构件

    • ConcreteComponent: 具体构件

    • Decorator: 抽象装饰类

    • ConcreteDecorator: 具体装饰类

    MquB5t.md.png

    在单一职责原则下,一个类不宜实现过多的功能,一个类能提供其基本功能,但在某些条件下又需要实现一些扩展功能,此时便可以使用继承或者装饰器模式,在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。 装饰器内部维护原始对象,增加逻辑以增加功能。

    public interface Cipher {
        String encrypt(String plainText);
    }
    /**
     * 自由实现,只提供简单加密
     */
    public class SimpleCipher implements Cipher {
    
        @Override
        public String encrypt(String plainText){
            StringBuilder res=new StringBuilder();
            for(char c:plainText.toCharArray()){
                res.append((int) c);
            }
            return res.toString();
        }
    }
    /**
     * 装饰器,增强功能
     */
    public class CipherDecorator implements Cipher {
        private Cipher cipher;
        public CipherDecorator(Cipher cipher){
            this.cipher=cipher;
        }
    
        public void setCipher(Cipher cipher) {
            this.cipher = cipher;
        }
    
        @Override
        public String encrypt(String plainText){
            return cipher.encrypt(plainText);
        }
    
        public String complexEncrypt(String plainText){
            StringBuilder res=new StringBuilder(encrypt(plainText));
            res.reverse();
            return res.toString();
        }
    
    }
    
    public class DecoratorPatternTest {
        public static void main(String[] args){
            Cipher cipher=new SimpleCipher();
            System.out.println(cipher.encrypt("HelloWorld"));
    
            CipherDecorator cipherDecorator=new CipherDecorator(cipher);
            System.out.println(cipherDecorator.complexEncrypt("HelloWorld"));
    
        }
    }
    /*结果为
    7210110810811187111114108100
    0018014111117811180180110127
    */
    

    装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。

    • 可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的装饰器,从而实现不同的行为。

    • 通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。

    • 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”。

    装饰模式的缺点

    • 使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,同时还将产生很多具体装饰类。这些装饰类和小对象的产生将增加系统的复杂度,加大学习与理解的难度。

    • 这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。

    外观模式

    外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。又称为门面模式。

    MOg9lq.md.png

    模式结构:

    • Facade: 外观角色

    • SubSystem:子系统角色

    引入一个外观对象,为子系统提供统一入口,即提供一个统一的访问门面,Facade内部维护子对象,通过方法返回系统,客户端不用显式声明,而是通过该门面进行中间调用

    public class Fan {
        public void on(){
            System.out.println("风扇开");
        }
        public void off(){
            System.out.println("风扇关");
        }
    }
    public class Light {
        public void on(){
            System.out.println("灯开");
        }
        public void off(){
            System.out.println("灯关");
        }
    }
    public class Facade {
        private Fan fan=new Fan();
        private Light light=new Light();
    
        public Fan getFan() {
            return fan;
        }
    
        public Light getLight() {
            return light;
        }
    }
    
    public class FacadePatternTest {
        public static void main(String[] args){
            Facade facade=new Facade();
            facade.getFan().on();
            facade.getLight().on();
        }
    }
    /*
    风扇开
    灯开
    */
    

    外观模式主要优点在于对客户屏蔽子系统组件,减少了客户处理的对象数目并使得子系统使用起来更加容易,它实现了子系统与客户之间的松耦合关系。

    其缺点在于不能很好地限制客户使用子系统类,而且在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。 外观模式适用情况包括:要为一个复杂子系统提供一个简单接口;客户程序与多个子系统之间存在很大的依赖性;在层次化结构中,需要定义系统中每一层的入口,使得层与层之间不直接产生联系。

    享元模式

    即Flyweight Pattern,将具有相同性质的某个成员集合称为共享内容,如字母a-z都是字母,组合起来就是一个具有抽象共同特征的集合,外部使用时需要这些细颗粒度的元素组合成一个新的对象(字符串),为避免系统消耗,创建一个享元工厂,内部维护一个享元池,这个享元池就是细颗粒度的集合,当需要字母时通过工厂获取,工厂先判断池中是否有字母,如果存在直接返回就好,如果不存在则创建并返回。

    划分颗粒度,将共享内容维护在一个池,外部使用时先判断池中有没有,有则返回,没有则创建并返回,享元模式的目的就是使用共享技术来实现大量细粒度对象的复用,享元工厂与享元池是其核心。

    MvooYq.md.png

    模式结构:

    • Flyweight: 抽象享元类 ,享元对象(细颗粒度)的接口定义

    • ConcreteFlyweight: 具体享元类,享元对象(细颗粒度)的具体实现

    • UnsharedConcreteFlyweight: 非共享具体享元类 ,不使用享元模式时的实现,多次复用细颗粒度状态

    • FlyweightFactory: 享元工厂类,内部维护享元池,维护细颗粒度集合

    享元模式是一个考虑系统性能的设计模式,通过使用享元模式可以节约内存空间,提高系统的性能

    /**
     * 享元对象的接口定义
     */
    public interface NetworkDevice {
        String getType();
    }
    public class Switch implements NetworkDevice{
        @Override
        public String getType() {
            return "Switch";
        }
    }
    public class Hub implements NetworkDevice {
        private String type="Hub";
        @Override
        public String getType() {
            return type;
        }
    }
    public class DeviceFactory {
        private static List<NetworkDevice> deviceList=new ArrayList<>();
    
        public static NetworkDevice getNetworkDevice(String type){
            for(NetworkDevice networkDevice:deviceList){
                if(networkDevice.getType().equals(type)){
                    return networkDevice;
                }
            }
            if(type.equals("Switch"))deviceList.add(new Switch());
            else deviceList.add(new Hub());
            return deviceList.get(deviceList.size()-1);
        }
    }
    public class FlyweightPattern {
        public static void main(String[] args){
            System.out.println(DeviceFactory.getNetworkDevice("Hub").getType());
            System.out.println(DeviceFactory.getNetworkDevice("Switch").getType());
        }
    }
    /*
    Hub
    Switch
    */
    

    享元模式的优点在于它可以极大减少内存中对象的数量,使得相同对象或相似对象在内存中只保存一份。 享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享

    享元模式使得系统更加复杂,需要分离出内部状态和外部状态,这使得程序的逻辑复杂化。

    享元模式适用情况包括:一个系统有大量相同或者相似的对象,由于这类对象的大量使用,造成内存的大量耗费; 对象的大部分状态都可以外部化,可以将这些外部状态传入对象中(也就是将大对象进行细粒度分解,使用对象时要按需注入数据);多次重复使用享元对象。

    代理模式

    在某些情况下,一个客户不想或者不能直接引用一个对 象,此时可以通过一个称之为“代理”的第三者来实现间接引用。代理对象可以在客户端和目标对象之间起到中介的作用,并且可以通过代理对象去掉客户不能看到的内容和服务或者添加客户需要的额外服务。

    通过引入一个新的对象来实现对真实对象的操作或者将新的对象作为真实对象的一个替身,这种实现机制即为代理模式,通过引入代理对象来间接访问一个对象,这就是代理模式的模式动机,即给某一个对象提供一个代理,并由代理对象控制对原对象的引用。

    代理模式包含如下角色:

    • Subject: 抽象主题角色,可以是抽象类也可以是接口,定义抽象功能

    • Proxy: 代理主题角色

    • RealSubject: 真实主题角色

    静态代理的一般模式为

    QSdseO.md.png

    静态代理就是通过构造一个代理类,内部维护一个代理对象,为该对象的方法提供增强过后的功能,或根据需要设置功能的访问权,静态代理只能是一个具体的类有一个代理角色,功能不强。

    public class TestDemo {
        interface IService{
            void say();
        }
        static class A implements IService{
            public void say(){
                System.out.println("I am A");
            }
        }
        static class Proxy implements IService{
            private A a;
            public Proxy(A a){
                this.a=a;
            }
    
            public void say(){
                System.out.println("我是代理,我先加点东西");
                a.say();
                System.out.println("我是代理,方法调用结束后加点东西");
            }
        }
         
        public static void main(String[] args){
            System.out.println("正常实现A");
            A a=new A();
            a.say();
            System.out.println("代理强化A");
            Proxy aProxy=new Proxy(a);
            aProxy.say();
        }
    
    }
    /*
    执行结果为:
    正常实现A
    I am A
    代理强化A
    我是代理,我先加点东西
    I am A
    我是代理,方法调用结束后加点东西
    */
    

    代理模式能够协调调用者和被调用者,在一定程度上降低了系统的耦合度,保护代理可以控制对真实对象的使用权限,代理模式应用的主要类型有:

    远程(Remote)代理:为一个位于不同的地址空间的对象提供一个本地 的代理对象,这个不同的地址空间可以是在同一台主机中,也可是在 另一台主机中,远程代理又叫做大使(Ambassador)。

    • 虚拟(Virtual)代理:如果需要创建一个资源消耗较大的对象,先创建一 个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。

    • Copy-on-Write代理:它是虚拟代理的一种,把复制(克隆)操作延迟 到只有在客户端真正需要时才执行。一般来说,对象的深克隆是一个 开销较大的操作,Copy-on-Write代理可以让这个操作延迟,只有对象 被用到的时候才被克隆。
    • 保护(Protect or Access)代理:控制对一个对象的访问,可以给 不同的用户提供不同级别的使用权限。

    • 缓冲(Cache)代理:为某一个目标操作的结果提供临时的存储空 间,以便多个客户端可以共享这些结果。

    • 防火墙(Firewall)代理:保护目标不让恶意用户接近。

    • 同步化(Synchronization)代理:使几个用户能够同时使用一个对 象而没有冲突。

    • 智能引用(Smart Reference)代理:当一个对象被引用时,提供 一些额外的操作,如将此对象被调用的次数记录下来等。

    动态代理:

        说实话,静态代理的功能一点也不强,如果A,B,C...有许多类都需要在方法前加一点东西,不可能为每一个类都写一个对应的代理进行强化,如果能动态生成就好了。有需求必然有变化,java为应对需求推出了类java.lang.reflect.Proxy同时对动态代理设置了一定的规范。
    
          首先,将需要切入的方法抽取出来放在接口中,A照样实现接口从而实现方法。
    
          然后,写一个通用代理类,代理类Proxy内部不在维护A,而是维护一个Object,该代理类要实现通用的代理接口InvocationHandler,该接口只有一个方法invoke()。Proxy内部维护Object,这个Object就可以代表任何上述接口,想要执行接口方法JVM就会自动调用内部invoke()方法。
    

    invoke有三个参数:Object proxy,JVM自动生成的对应接口对象,一般不用管它、Method method:要切入的方法、Object args:切入的方法的参数

    invoke()返回一个Object,在invoke()内部要使用method.invoke(obj,args)调用内部维护的Object的原始方法,然后返回

        最后想用代理时创建代理即可,要创建代理,简单,调用(XXX)Proxy.newProxyInstance()即可,直接创建代理并进行强制类型转换
    

    该方法也有三个参数:ClassLoader loader:任意上述接口对象的类加载器、Class<?>[] interfaces:任意上述接口对象的数组、InvocationHandler h:代理

        简而言之,动态代理就是在运行时动态生成指定接口的代理用于强化接口方法,就是动态生成静态代理,在内部使用时,每次通过Proxy.newProxyInstance()指定接口与通用代理类,通用代理类的构造器传入具体的接口实现类作为参数。JVM内部自动生成一个与指定接口绑定的代理类,名为ProxyX(X是自增数字),该代理类被传入通用代理类的invoke方法中,同时将绑定到的实现接口的具体类的方法method与方法参数args一并传入invoke,然后执行invoke()中的代码,要调用原方法,须使用method.invoke(obj,args),返回一个Object作为结果,返回该结果即可
    
    public class TestDemo {
        interface IA{
            void sayA();
        }
        interface IB{
            void sayB();
        }
        static class A implements IA{
            public void sayA(){
                System.out.println("I am A");
            }
        }
        static class B implements IB{
            public void sayB(){
                System.out.println("I am B");
            }
        }
        static class MyProxy implements InvocationHandler {
            private Object obj;
            public MyProxy(Object obj){
                this.obj=obj;
            }
    
            public Object invoke(Object proxy, Method method,Object[] args){
                System.out.println("我是自动创建的代理"+proxy.getClass().getName());
                System.out.println("我是代理,我需要处理方法"+method.getName());
         
                if(args!=null)System.out.println("我是代理,该方法有参数"+args.length+"个");
                try{
                    Object result=method.invoke(obj,args);//执行原始object方法
                    return result;
                }catch (Exception e){
                    e.printStackTrace();
                }
                return null;
            }
        }
         
        public static void main(String[] args){
            IA a=(IA) Proxy.newProxyInstance(IA.class.getClassLoader(),new Class<?>[]{IA.class},new MyProxy(new A()));
            a.sayA();
            IB b=(IB)Proxy.newProxyInstance(IB.class.getClassLoader(),new Class<?>[]{IB.class},new MyProxy(new B()));
            b.sayB();
        }
    
    }
    

    结果为:
    我是自动创建的代理mapper.$Proxy0

    我是代理,我需要处理方法sayA

    I am A

    我是自动创建的代理mapper.$Proxy1

    我是代理,我需要处理方法sayB

    I am B

    展开全文
  • 设计模式 - 结构型设计模式小结

    万次阅读 多人点赞 2019-02-02 17:29:01
    请点击http://www.captainbed.net 结构型模式旨在通过改变代码结构来达到解耦的目的,使得我们的代码容易维护和扩展。 前面,我们说了代理模式、适配器模式、桥梁模式、装饰模式、门面模式、组合模式和享元模式。...

    分享一个大牛的人工智能教程。零基础!通俗易懂!风趣幽默!希望你也加入到人工智能的队伍中来!请点击http://www.captainbed.net

    结构型模式旨在通过改变代码结构来达到解耦的目的,使得我们的代码容易维护和扩展。

    前面,我们说了代理模式、适配器模式、桥梁模式、装饰模式、门面模式、组合模式和享元模式。读者是否可以分别把这几个模式说清楚了呢?在说到这些模式的时候,心中是否有一个清晰的图或处理流程在脑海里呢?

    代理模式是做方法增强的,适配器模式是把鸡包装成鸭这种用来适配接口的,桥梁模式做到了很好的解耦,装饰模式从名字上就看得出来,适合于装饰类或者说是增强类的场景,门面模式的优点是客户端不需要关心实例化过程,只要调用需要的方法即可,组合模式用于描述具有层次结构的数据,享元模式是为了在特定的场景中缓存已经创建的对象,用于提高性能。

    展开全文
  • 设计模式(二)结构型模式

    万次阅读 2018-08-04 20:58:08
    Java 中一般认为有23 种设计模式,我们不需要所有的都会,但是其中常用的几种设计模式应该去掌握。 下面列出了所有的设计模式。... 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模...

    Java 中一般认为有23 种设计模式,我们不需要所有的都会,但是其中常用的几种设计模式应该去掌握。
    下面列出了所有的设计模式。需要掌握的设计模式我单独列出来了,当然能掌握的越多越好。
    总体来说设计模式分为三大类:
    创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
    结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
    行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

    总结:每一种设计模式,代码去理解实现方式,根据结构图记忆理解。本文没有给出具体代码,可以在码云下载代码使用,本文适合不断翻看理解这些设计模式
    码云代码:https://gitee.com/huopusa/arithmetic.git

    六、适配器模式 adapter

    分类
    类适配器、对象适配器、接口适配器
    UML图
    这里写图片描述
    适配器模式应用场景
    类适配器与对象适配器的使用场景一致,仅仅是实现手段稍有区别,二者主要用于如下场景
    (1)想要使用一个已经存在的类,但是它却不符合现有的接口规范,导致无法直接去访问,这时创建一个适配器就能间接去访问这个类中的方法。
    (2)我们有一个类,想将其设计为可重用的类(可被多处访问),我们可以创建适配器来将这个类来适配其他没有提供合适接口的类。
    接口适配器使用场景:
    1)想要使用接口中的某个或某些方法,但是接口中有太多方法,我们要使用时必须实现接口并实现其中的所有方法,可以使用抽象类来实现接口,并不对方法进行实现(仅置空),然后我们再继承这个抽象类来通过重写想用的方法的方式来实现。这个抽象类就是适配器。
    参照博文:
    https://blog.csdn.net/yujin753/article/details/46287643
    http://www.cnblogs.com/V1haoge/p/6479118.html

    七、装饰器模式 decorator

    https://www.cnblogs.com/jzb-blog/p/6717349.html

    Component为统一接口这里写图片描述,也是装饰类和被装饰类的基本类型。
    ConcreteComponent为具体实现类,也是被装饰类,他本身是个具有一些功能的完整的类。
    Decorator是装饰类,实现了Component接口的同时还在内部维护了一个ConcreteComponent的实例,并可以通过构造函数初始化。而Decorator本身,通常采用默认实现,他的存在仅仅是一个声明:我要生产出一些用于装饰的子类了。而其子类才是赋有具体装饰效果的装饰产品类。
    ConcreteDecorator是具体的装饰产品类,每一种装饰产品都具有特定的装饰效果。可以通过构造器声明装饰哪种类型的ConcreteComponent,从而对其进行装饰。
    优点:
    装饰器模式是一种用于代替继承的技术,无需通过继承增加子类就能扩展对象的新功能。使用对象的关联关系代替继承关系,更加灵活,同时避免类型体系的快速膨胀。
    代码:

    public interface ICar {
        void move();
    }
    //ConcreteComponent 具体构件角色(真实对象)
    class Car implements ICar {
    
        @Override
        public void move() {
            System.out.println("陆地上跑!");
        }
    
    }
    class SuperCar implements ICar {
        private ICar car;
        public SuperCar(ICar car) {
            this.car = car;
        }
        @Override
        public void move() {
            car.move();
        }
    }
    //ConcreteDecorator具体装饰角色
    class FlyCar extends SuperCar {
        public FlyCar(ICar car) {
            super(car);
        }
        public void fly() {
            System.out.println("天上飞");
        }
        @Override
        public void move() {
            super.move();
            fly();
        }
    }
    //ConcreteDecorator具体装饰角色
    class WaterCar extends SuperCar {
        public WaterCar(ICar car) {
            super(car);
        }
        public void swim() {
            System.out.println("水里游");
        }
        @Override
        public void move() {
            super.move();
            swim();
        }
    }
    //ConcreteDecorator具体装饰角色
    class AICar extends SuperCar {
        public AICar(ICar car) {
            super(car);
        }
        public void autoMove() {
            System.out.println("自动跑");
        }
        @Override
        public void move() {
            super.move();
            autoMove();
        }

    测试客户端:

    public class Client {
        public static void main(String[] args) {
            ICar car = new Car();
            car.move();
    
            System.out.println("------------增加新的功能:飞行");
            ICar flycar = new FlyCar(car);
            flycar.move();
    
            System.out.println("------------增加新的功能:水里游");
            ICar waterCar = new WaterCar(car);
            waterCar.move();
    
            System.out.println("------------增加两个新的功能,飞行,水里游");
            ICar waterCar2 = new WaterCar(flycar);
            waterCar2.move();
    
            System.out.println("------------累加3个新的功能,飞行,水里游,自动驾驶");
            ICar superCar = new AICar(waterCar2);
            superCar.move();
        }
    }

    运行结果:这里写图片描述

    八、代理模式 proxy

    特点:
    1、 执行者、 被代理人
    2、 对于被代理人来说, 这件事情是一定要做的, 但是我自己又不想做或者没有时间做, 找代理。
    3、 需要获取到被代理的人个人资料。
    4、关心过程
    例子:
    租房中介: 中介和你
    火车票黄牛: 黄牛和你
    媒人: 媒婆和你
    明星经纪人: 经纪人和明星 刘德华要开演唱会(长沙) 、 准备工作和善后工作

    AOP中使用场景
    事务代理(声明式事务, 哪个方法需要加事务, 哪个方法不需要加事务)
    日志监听
    假如我们定义了一个service 方法
    开启一个事务(open) 代理来做
    事务的执行 执行我们的service方法
    监听到是否有异常, 可能需要根据异常的类型来决定这个事务是否要回滚还是继续提交 代理来做
    (commit/rollback) 代理来做
    事务要关闭(close) 代理来做

    参照
    https://blog.csdn.net/qq_33214833/article/details/70230891
    注:代理模式应用比较广泛,使代码开发更加灵活

    九、外观模式 Facade (门面模式)

    概念:
    外观模式(Facade),他隐藏了系统的复杂性,并向客户端提供了一个可以访问系统的接口。这种类型的设计模式属于结构性模式。为子系统中的一组接口提供了一个统一的访问接口,这个接口使得子系统更容易被访问或者使用。
    uml图
    这里写图片描述
     简单来说,该模式就是把一些复杂的流程封装成一个接口供给外部用户更简单的使用。这个模式中,设计到3个角色。
      1).门面角色:外观模式的核心。它被客户角色调用,它熟悉子系统的功能。内部根据客户角色的需求预定了几种功能的组合。
      2).子系统角色:实现了子系统的功能。它对客户角色和Facade时未知的。它内部可以有系统内的相互交互,也可以由供外界调用的接口。
      3).客户角色:通过调用Facede来完成要实现的功能
    注:因门面模式Spring+接口调用应用太广泛,没有写具体代码
    十、桥接模式 bridge
    https://www.cnblogs.com/lixiuyu/p/5923160.html

    1.桥接模式的优点
    (1)实现了抽象和实现部分的分离
    桥接模式分离了抽象部分和实现部分,从而极大的提供了系统的灵活性,让抽象部分和实现部分独立开来,分别定义接口,这有助于系统进行分层设计,从而产生更好的结构化系统。对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了。
    (2)更好的可扩展性
    由于桥接模式把抽象部分和实现部分分离了,从而分别定义接口,这就使得抽象部分和实现部分可以分别独立扩展,而不会相互影响,大大的提供了系统的可扩展性。
    (3)可动态的切换实现
    由于桥接模式实现了抽象和实现的分离,所以在实现桥接模式时,就可以实现动态的选择和使用具体的实现。
    (4)实现细节对客户端透明,可以对用户隐藏实现细节。
    2.桥接模式的缺点
    (1)桥接模式的引入增加了系统的理解和设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计和编程。
    (2)桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围有一定的局限性。
    3.桥接模式的使用场景
    (1)如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。
    (2)抽象化角色和实现化角色可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将一个抽象化子类的对象和一个实现化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合。
    (3)一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。
    (4)虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者。
    (5)对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用
    代码

    /**
     * 定义接口--被实现者
     */
    public interface Implementor {
        public void operation();
    }
    /**
     * 实现者A
     */
    public class ConcreateImplementorA implements Implementor {
        @Override
        public void operation() {
            System.out.println("这个是ConcreateImplementorA的operation方法");
        }
    }
    /**
     * 实现者B
     */
    public class ConcreateImplementorB implements Implementor {
        @Override
        public void operation() {
            System.out.println("这个是ConcreateImplementorB的operation方法");
        }
    }
    /**
     *  桥接类
     */
    public abstract class Abstraction {
        private Implementor implementor;
        public Implementor getImplementor() {
            return implementor;
        }
        public void setImplementor(Implementor implementor){
            this.implementor = implementor;
        }
        // 引用接口
        protected void operation(){
            implementor.operation();
        }
    }
    /**
     * 桥接实现类
     */
    public class RefinedAbstraction extends Abstraction {
        @Override
        protected void operation() {
            super.operation();
        }
    }
    /**
     * client 调用测试
     */
    public class BridgeTest {
        public static void main(String[] args) {
            Abstraction abstraction = new RefinedAbstraction();
    
            //调用第一个实现类
            abstraction.setImplementor(new ConcreateImplementorA());
            abstraction.operation();
    
            //调用第二个实现类
            abstraction.setImplementor(new ConcreateImplementorB());
            abstraction.operation();
        }
    }

    十一、组合模式 Composite

    1、UML结构
    这里写图片描述

    实例图这里写图片描述

    2、角色组成
    抽象构件角色(component):是组合中的对象声明接口,在适当的情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理Component子部件。这个接口可 以用来管理所有的子对象。(可选)在递归结构中定义一个接口,用于访问一个父部件,并在合适的情况下实现它。
    树叶构件角色(Leaf):在组合树中表示叶节点对象,叶节点没有子节点。并在组合中定义图元对象的行为。
    树枝构件角色(Composite):定义有子部件的那些部件的行为。存储子部件。在Component接口中实现与子部件有关的操作。
    客户角色(Client):通过component接口操纵组合部件的对象。

    3、组合模式的优缺点
    优点:
    组合模式使得客户端代码可以一致地处理对象和对象容器,无需关系处理的单个对象,还是组合的对象容器。
    将”客户代码与复杂的对象容器结构“解耦。
    可以更容易地往组合对象中加入新的构件。
    缺点: 使得设计更加复杂。客户端需要花更多时间理清类之间的层次关系。(这个是几乎所有设计模式所面临的问题)。
    注意的问题:
    有时候系统需要遍历一个树枝结构的子构件很多次,这时候可以考虑把遍历子构件的结构存储在父构件里面作为缓存。
    客户端尽量不要直接调用树叶类中的方法(在我上面实现就是这样的,创建的是一个树枝的具体对象;),而是借用其父类(Graphics)的多态性完成调用,这样可以增加代码的复用性。
    4、组合模式的使用场景
    在以下情况下应该考虑使用组合模式:
    当想表达对象的部分-整体的层次结构时。
    希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象时。
    参考:https://www.cnblogs.com/snaildev/p/7647190.html

    十二、享元模式 Flyweight Pattern

    http://www.cnblogs.com/java-my-life/archive/2012/04/26/2468499.html
    享元模式:以共享的方式高效的支持大量的细粒度对象。通过复用内存中已存在的对象,降低系统创建对象实例的性能消耗。
    java的 String 类型就是享元模式
    这里写图片描述
    单纯享元模式所涉及到的角色如下:
      ●  抽象享元(Flyweight)角色 :给出一个抽象接口,以规定出所有具体享元角色需要实现的方法。
      ●  具体享元(ConcreteFlyweight)角色:实现抽象享元角色所规定出的接口。如果有内蕴状态的话,必须负责为内蕴状态提供存储空间。
      ●  享元工厂(FlyweightFactory)角色 :本角色负责创建和管理享元角色。本角色必须保证享元对象可以被系统适当地共享。当一个客户端对象调用一个享元对象的时候,享元工厂角色会检查系统中是否已经有一个符合要求的享元对象。如果已经有了,享元工厂角色就应当提供这个已有的享元对象;如果系统中没有一个适当的享元对象的话,享元工厂角色就应当创建一个合适的享元对象。
    复合享元角色所涉及到的角色如下:
      ●  抽象享元(Flyweight)角色 :给出一个抽象接口,以规定出所有具体享元角色需要实现的方法。
      ●  具体享元(ConcreteFlyweight)角色:实现抽象享元角色所规定出的接口。如果有内蕴状态的话,必须负责为内蕴状态提供存储空间。
      ●  复合享元(ConcreteCompositeFlyweight)角色 :复合享元角色所代表的对象是不可以共享的,但是一个复合享元对象可以分解成为多个本身是单纯享元对象的组合。复合享元角色又称作不可共享的享元对象。
      ●  享元工厂(FlyweightFactory)角色 :本角 色负责创建和管理享元角色。本角色必须保证享元对象可以被系统适当地共享。当一个客户端对象调用一个享元对象的时候,享元工厂角色会检查系统中是否已经有 一个符合要求的享元对象。如果已经有了,享元工厂角色就应当提供这个已有的享元对象;如果系统中没有一个适当的享元对象的话,享元工厂角色就应当创建一个 合适的享元对象。
    享元模式的优缺点
      享元模式的优点在于它大幅度地降低内存中对象的数量。但是,它做到这一点所付出的代价也是很高的:
      ●  享元模式使得系统更加复杂。为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑复杂化。
      ●  享元模式将享元对象的状态外部化,而读取外部状态使得运行时间稍微变长。

    享元模式实例
    其实在Java中就存在这种类型的实例:String。
    Java中将String类定义为final(不可改变的),JVM中字符串一般保存在字符串常量池中,这个字符串常量池在jdk 6.0以前是位于常量池中,位于永久代,而在JDK 7.0中,JVM将其从永久代拿出来放置于堆中。
      我们使用如下代码定义的两个字符串指向的其实是同一个字符串常量池中的字符串值。

    String s1 = "abc";
    String s2 = "abc";

    如果我们以s1==s2进行比较的话所得结果为:true,因为s1和s2保存的是字符串常量池中的同一个字符串地址。这就类似于我们今天所讲述的享元模式,字符串一旦定义之后就可以被共享使用,因为他们是不可改变的,同时被多处调用也不会存在任何隐患。
    享元模式使用的场景:
        当我们项目中创建很多对象,而且这些对象存在许多相同模块,这时,我们可以将这些相同的模块提取出来采用享元模式生成单一对象,再使用这个对象与之前的诸多对象进行配合使用,这样无疑会节省很多空间。

    参照例子(更加形象的例子):https://www.cnblogs.com/V1haoge/p/6542449.html

    展开全文
  • Java设计模式结构型:桥接模式

    万次阅读 2021-09-13 17:26:40
    为了达到让抽象部分和实现部分独立变化的目的,桥接模式使用组合关系来代替继承关系,抽象部分拥有实现部分的接口对象,从而能够通过这个接口对象来调用具体实现部分的功能。也就是说,桥接模式中的桥接是一个单方向...

    一、什么是桥接模式:

            桥接,顾名思义,就是用来连接两个部分,使得两个部分可以互相通讯,桥接模式的作用就是为被分离的抽象部分和实现部分搭桥。在现实生活中一个物品在搭配不同的配件时会产生不同的动作和结果,例如一辆赛车搭配的是硬胎或者是软胎就能够在干燥的马路上行驶,而如果要在下雨的路面行驶,就需要搭配雨胎了,这种根据行驶的路面不同,需要搭配不同的轮胎的变化的情况,我们从软件设计的角度来分析,就是一个系统由于自身的逻辑,会有两个或多个维度的变化,而为了应对这种变化,我们就可以使用桥接模式来进行系统的解耦。 桥接模式将一个系统的抽象部分和实现部分分离,使它们都可以独立地进行变化,对应到上面就是赛车的种类可以相对变化,轮胎的种类可以相对变化,形成一种交叉的关系,最后的结果就是一种赛车对应一种轮胎就能够成功产生一种结果和行为。 

            桥接模式将系统的抽象部分与实现部分分离解耦,使他们可以独立的变化。为了达到让抽象部分和实现部分独立变化的目的,桥接模式使用组合关系来代替继承关系,抽象部分拥有实现部分的接口对象,从而能够通过这个接口对象来调用具体实现部分的功能。也就是说,桥接模式中的桥接是一个单方向的关系,只能够抽象部分去使用实现部分的对象,而不能反过来。 

            桥接模式符合“开闭原则”,提高了系统的可拓展性,在两个变化维度中任意扩展一个维度,都不需要修改原来的系统;并且实现细节对客户不透明,可以隐藏实现细节。但是由于聚合关系建立在抽象层,要求开发者针对抽象进行编程,这增加系统的理解和设计难度。

            所以,桥接模式一般适用于以下几种应用场景:

    • (1)系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,则可以通过桥接模式使他们在抽象层建立一个关联关系;
    • (2)系统不希望使用继承或因为多层次继承导致系统类的个数急剧增加时
    • (3)一个类存在两个独立变化的维度,而这两个维度都需要进行扩展。

    二、UML结构图:

    • 抽象化角色 Abstraction:定义抽象的接口,包含一个对实现化角色的引用,抽象角色的方法需要调用实现化角色;
    • 扩展抽象化角色 RefinedAbstraction:抽象化角色的子类,一般对抽象部分的方法进行完善和扩展,实现父类中的业务方法,并通过组合/聚合关系调用实现化角色中的业务方法
    • 实现化角色 Implementor:定义具体行为、具体特征的应用接口,供扩展抽象化角色使用,一般情况下是由实现化角色提供基本的操作,而抽象化角色定义基于实现部分基本操作的业务方法;
    • 具体实现化角色 ConcreteImplementor:完善实现化角色中定义的具体逻辑。

    三、代码实现:

    Implementor 接口类:

    public interface Implementor {
        void operationImpl();
    }

    ConcreteImplementor 接口实现类:

    public class ConcreteImplementorA implements Implementor{
        @Override
        public void operationImpl() {
            //具体实现
        }
    }
    
    public class ConcreteImplementorB implements Implementor{
        @Override
        public void operationImpl() {
            //具体实现
        }
    }

    Abstraction 抽象类:

    public abstract class Abstraction {
        private Implementor implementor;
    
        public Abstraction(Implementor implementor) {
            this.implementor = implementor;
        }
    
        public void operation() {
            implementor.operationImpl();
        }
    }

    RefinedAbstraction 抽象类的具体实现:

    public class RefinedAbstraction extends Abstraction{
        public RefinedAbstraction(Implementor implementor) {
            super(implementor);
        }
    
        public void refinedOperation() {
            //对 Abstraction 中的 operation 方法进行扩展
        }
    }

            看了这段通用代码之后,桥接模式的结构应该就很清楚了,需要注意的是 RefinedAbstraction 根据实际情况是可以有多个的。 当然上面的 UML 类图和通用代码只是最常用的实现方式而已,在实际使用中可能会有其他的情况,比如 Implementor 只有一个类的情况,虽然这时候可以不去创建 Implementor 接口,精简类的层次,但是我建议还是需要抽象出实现部分的接口。

    四、JDBC源码解析-桥接模式:

    该部分引用自:JDBC和桥接模式 - 枯落 - 博客园

            Java 中,我们使用 JDBC 连接数据库时,在各个数据库之间进行切换,基本不需要动太多的代码,原因就是使用了桥接模式,JDBC 提供统一接口,每种类型的数据库提供各自的实现,然后由桥接类创建一个连接数据库的驱动,使用某一个数据库的时候只需要切换一下就行。接下来我们就对 JDBC 的源码做下剖析:

    通过原生JDBC API连接MySQL数据库,则有如下示例代码:

    Class.forName("com.mysql.cj.jdbc.Driver");
    Connection conn = DriverManager.getConnection("jdbc:mysql://<host>:<port>/<database>");

    短短两行代码难以看出桥接模式的结构,下面先对源码进行一定的分析,理解各个类和接口之间的关系:

    1、源码分析:

    (1)Class.forName() 方法:

            该方法将返回与给定字符串名的类或接口相关联的 java.lang.Class 类对象,用于在程序运行时动态加载该类或该接口到当前线程中,如果 Class.forName() 加载的是一个类,也会执行类中包含的static { } 静态代码段

    (2)com.mysql.cj.jdbc.Driver 类:

            MySQL 将具体的 java.sql.Driver 接口的实现放到了 NonRegisteringDriver 中,com.mysql.cj.jdbc.Driver 类仅包含一段静态代码,具体类图如下:

            其中最关键的是静态代码段中的 DriverManager.registerDriver(new Driver()) ,它会在客户端调用Class.forName() 方法加载 com.mysql.cj.jdbc.Driver 类的同时被执行,Driver 类自身的一个实例被注册到 DriverManager(即保存到 DriverManager 的静态字段 registeredDrivers 内),注册过程的源码如下: 

    public static synchronized void registerDriver(java.sql.Driver driver, DriverAction da)
    throws SQLException {
      /* Register the driver if it has not already been added to our list */
      if(driver != null) {
        registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
      } else {
        // This is for compatibility with the original DriverManager
        throw new NullPointerException();
      }
      println("registerDriver: " + driver);
    }

            registeredDrivers 静态字段的类型是实现了 List 接口的 CopyOnWriteArrayList 类,它能够保存进一步封装 java.sql.Driver 接口的 DriverInfo 类实例,DriverInfo 类的声明代码如下:

    class DriverInfo {
      final Driver driver;
      DriverAction da;
      DriverInfo(Driver driver, DriverAction action) {
        this.driver = driver;
        da = action;
      }
      // ……
    }

    DriverInfo 还包装了 DriverAction,DriverAction 会在Driver被取消注册时被调用,在 MySQL 的 Driver 在向 DriverManager 进行注册时,DriverAction 被设置为 null

    (3)DriverManager 类:

    由上面的分析可得,Class.forName() 方法调用后,com.mysql.cj.jdbc.Driver 类被加载,并执行static { } 静态代码段,将 com.mysql.cj.jdbc.Driver 类实例注册到 DriverManager 中。然后,客户端会调用 DriverManager.getConnection() 方法获取一个 Connection 数据库连接实例,该方法的部分源码如下:

    private static Connection getConnection(String url, java.util.Properties info, Class<?> caller) throws SQLException {
      // ……
      for(DriverInfo aDriver : registeredDrivers) {
        // If the caller does not have permission to load the driver then
        // skip it.
        if(isDriverAllowed(aDriver.driver, callerCL)) {
          try {
            println(" trying " + aDriver.driver.getClass().getName());
            Connection con = aDriver.driver.connect(url, info);
            if (con != null) {
              // Success!
              println("getConnection returning " + aDriver.driver.getClass().getName());
              return (con);
            }
          } catch (SQLException ex) {
            if (reason == null) {
              reason = ex;
            }
          }
        } else {
          println(" skipping: " + aDriver.getClass().getName());
        }
      }
      // ……
    }

    DriverManager.getConnection() 方法会遍历 registeredDrivers 静态字段,获取字段内保存的每一个 Driver 来尝试响应客户端的数据库连接请求,若所有 Driver 都连接数据库失败,则提示连接失败信息

    (4)Connection接口:

    Connection 代表和特定数据库的连接会话,能够执行SQL语句并在连接的上下文中返回执行结果。因此,DriverManager.getConnection() 方法返回的 Connection 数据库连接实例根据不同的数据库有不同的实现,MySQL 的 Connection 接口实现关系如下:

     2、源码类图:

    根据源码的分析,绘制类图如下:

     对 Driver 和 Connection 进行抽象,绘制类图如下:

            桥接模式通过聚合关系代替继承关系,实现抽象化和实现化部分的解耦。以上述 JDBC 在 MySQL 中的简略类图为例,抽象化部分有 DriverManager,实现化部分有 Driver 接口和 Connection 接口。对于不同的数据库,Driver接口和Connection接口都有自己独特的实现类。

            但是,和 Driver 接口不同的是,Connection 接口与 DriverManager 类的关系只是联系较弱的依赖关系,并不符合桥接模式的定义和特点。因此,在考虑桥接模式的情况下,可以再次将类图进行简化:

     最后,我们将其它数据库的Driver接口实现也考虑在内,绘制类图如下:

            桥接模式中的实现化角色 (Implementor) 对应上图的 Driver 接口,具体实现化 (Concrete Implementor) 角色对应 MysqlDriver、OracleDriver 和 MariadbDriver,扩展抽象化 (Refined Abstraction) 角色对应 DriverManager,不具有抽象化 (Abstraction) 角色作为扩展抽象化角色的父类

    3、对 JDBC 的观点:

    (1)观点一:JDBC 的桥接模式是一中简化的桥接模式

            桥接模式的主要应用场景是某个类存在两个独立变化的维度,且这两个维度都需要进行扩展,而现在仅有 Driver 一个变化维度,DriverManager 没有抽象化父类,它本身也没有任何子类,因此我认为,在 JDBC 中,是一种简化的桥接模式。

            倘若 JDBC 针对 Connection 接口的设计不是将它作为 Driver 和 DriverManager 的"依赖"来处理,而是也作为一个变化的维度加入到桥接模式,或许能够更好地体现JDBC对桥接模式的实现,一种"假想"的桥接模式如下:

     (2)观点二:JDBC采用的是策略模式而不是桥接模式

    问题来源知乎:jdbc是桥接模式还是策略模式? - 知乎

            因为这确实和策略模式十分相似,如果把桥接模式的抽象部分简化来看,不去设计Abstraction,也就是用 Refined Abstraction 代替 Abstraction,那么就类似于策略模式的 Context 来使用接口的对象。

            但是,桥接模式和策略模式的目的是不一样的,策略模式属于对象行为模式(描述对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,以及怎样分配职责),它的目的是封装一系列的算法,使得算法可以相互替代,并在程序运行的不同时刻选择合适的算法。而桥接模式属于对象结构模式(描述如何将对象按某种布局组成更大的结构),它的目的是将抽象与实现分离,使它们可以独立变化

            因此,从设计的目的来看,JDBC采用的并不是策略模式,在一段程序中数据库驱动并不存在频繁地相互替换

    (3)观点三:变化的维度一个是平台,另一个是数据库

    问题来源:https://www.unclewang.info/learn/java/771/?tdsourcetag=s_pctim_aiomsg

            这是我认同的一个观点,引用原文的话:变的是平台和数据库,平台在 JVM 这个层面就解决了,因为所有操作系统 Java 基本都会提供对应JDK,这也是 "Once Write,Run AnyWhere" 的原因。而数据库则是依托公司的具体实现,各个公司都提供对应的 Driver 类,我用 DriverManager 类进行懒加载.

            考虑数据库的实际应用场景,我们可能在不同的操作系统上使用不同的数据库,但是JVM的平台无关性使得我们不再有操作系统层面上的变化。假设不存在JVM,那么不同的客户端加载和运行数据库驱动程序的代码自然也各有不同,即 DriverManager 会因操作系统的变化而变化,不同的操作系统可以有不同的注册 Driver 的方式,不过因为存在JVM,我们现在不再有"平台"这一变化维度了

    (4)观点四:变化的维度一个是客户端应用系统,另一个是数据库

    问题来源:java设计模式-桥梁模式(桥接模式 Bridge) - 简书

            一个比较独特的观点,引用原文的话:应用系统作为一个等级结构,与 JDBC 驱动器这个等级结构是相对独立的,它们之间没有静态的强关联。应用系统通过委派与JDBC驱动器相互作用,这是一个桥梁模式的例子。

            原文笔者不认为 DriverManager 作为 Refined Abstraction 角色存在,而是视作两个变化维度之间的一个"过渡",原本的"桥"是 Abstraction 和 Implementor 之间的组合/聚合关系,而现在DriverManager 类本身成为了"桥",可以看作是桥梁模式的一个变体

    (5)观点五:变化的维度一个是 Driver,一个是 Connection:

            如果从观点四的原文笔者的角度看,把 DriverManager 类本身作为"桥",那么我们还可以提出一种新的观点,绘制类图如下:


    设计模式系列文章:

    Java设计模式之创建型:工厂模式详解(简单工厂+工厂方法+抽象工厂)

    Java设计模式之创建型:建造者模式

    Java设计模式之创建型:单例模式

    Java设计模式之创建型:原型模式

    Java设计模式之结构型:适配器模式

    Java设计模式之结构型:装饰器模式

    Java设计模式之结构型:代理模式

    Java设计模式之结构型:桥接模式

    Java设计模式之结构型:外观模式

    Java设计模式之结构型:组合模式

    Java设计模式之结构型:享元模式

    Java设计模式之行为型:策略模式

    Java设计模式之行为型:模板方法模式

    Java设计模式之行为型:责任链模式

    Java设计模式之行为型:观察者模式

    Java设计模式之行为型:访问者模式

    Java设计模式之行为型:中介者模式

    Java设计模式之行为型:命令模式

    Java设计模式之行为型:状态模式

    Java设计模式之行为型:备忘录模式

    Java设计模式之行为型:迭代器模式

    Java设计模式之行为型:解释器模式


    参考博客:

    JDBC和桥接模式 - 枯落 - 博客园

    java/android 设计模式学习笔记(8)---桥接模式_Shawn_Dut的专栏-CSDN博客_桥接模式

    JAVA开发的23种设计模式之 --- 桥接模式_叶孤心的专栏-CSDN博客

    展开全文
  • 设计模式——结构型模型

    千次阅读 2019-04-23 18:23:33
    1. 装饰者模式(Decorator) 2. 代理模式(Proxy) 3. 组合模式(Composite) 4. 桥接模式(Bridge) 5. 适配器模式(Adapter) 6. 蝇量模式(Flyweight) 7. 外观模式(Facade)
  • 设计模式-结构型模式总结

    千次阅读 2017-07-25 20:52:12
    结构型模式:主要用于描述如何组合类和对象以获得更大的结构。
  • 设计模式--结构型模式比较

    千次阅读 2012-08-22 21:21:27
    结构型模式为了组合类和对象以获得更大的结构而产生的一种指导性方法。(A structural design pattern serves as a blueprint for how different classes and objects are combined to form larger structures.)与...
  • 【面向对象】——设计模式结构型模式

    千次阅读 热门讨论 2014-12-28 11:27:18
    结构型模式包括:适配器、装饰、桥接、组合、享元、代理、外观模式。 适配器模式: 将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
  • 这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。我们通过下面的实例来演示装饰器模式的用法。...
  • 设计模式:结构型设计模式(下)

    千次阅读 2019-12-30 21:14:15
    * 装饰模式在不改变原有类结构的情况下向现有对象添加新功能 * 使用一个会变颜色的圆圈来举例 */ public class DecoratingMode { public static void main(String[] args) { Shape circle = new ...
  • 序号 模式 一句话说明 1 桥(Bridge) 将“抽象”和“实现”自由搭配。 2 轻量(Flyweight) 轻松地处理“大量”对象。 3 外观(Façade) 同时提供简单接口和复杂接口。 4 装饰者(Decorator) 不改变接口但要增强...
  • 这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。我们通过下面的实例来演示组合模式的用法。实例演示了一个组织中员工的层次...
  • 一、创建: 创建模式,就是创建对象的模式,抽象了实例化的过程。它帮助一个系统独立于如何创建、组合和表示它的那些对象。... 严格意义来说,不属于GOF23种设计模式 工厂方法模式 只...
  • 结构型模式 适配器模式、桥接模式、组合模式、装饰器模式、外观模式、向元模式、代理模式
  • 设计模式 - 结构型设计模式 - 适配器模式(Java)

    万次阅读 多人点赞 2019-01-23 20:24:55
    对象适配器模式 来看一个《Head First 设计模式》中的一个例子,我们稍微修改了一下,看看怎么将鸡适配成鸭,这样鸡也能当鸭来用。因为,现在鸭这个接口,我们没有合适的实现类可以用,所以需要适配器。 public ...
  • Java设计模式

    千次阅读 多人点赞 2019-09-03 23:20:31
    工厂模式一般分为简单工厂、工厂、抽象工厂3钟情况,属于创建型设计模式。 2.生成器模式 3.观察者模式 4.桥接模式 5.代理模式 6.状态模式 7.访问者模式 8.命令模式 9.装饰器...
  • 博主在大三的时候有上过...总体来说设计模式分为三大类:创建型模式、结构型模式和行为型模式。博主的上一篇文章已经提到过创建型模式,此外该文章还有设计模式概况和设计模式的六大原则。设计模式的六大原则是设计模式
  • 设计模式 - 结构型设计模式 - 组合模式(Java)

    万次阅读 多人点赞 2019-02-03 11:44:16
    请点击http://www.captainbed.net 组合模式用于表示具有层次结构的数据,使得我们对单个对象和组合对象的访问具有一致性。 直接看一个例子吧,每个员工都有姓名、部门、薪水这些属性,同时还有下属员工集合(虽然...
  • 设计模式可以分为创建型、结构型和行为型。 创建型 对类的现实化进行了抽象,能够使软件模块做到与对象的创建和组织无关。 功能:类的创建 创建型: 单例模式、抽象工厂模式、建造者模式、工厂模式、原型模式 结构型 ...
  • 设计模式-对象行为模式

    千次阅读 2018-04-19 19:24:03
    对象行为型模式】涉及到算法和对象间职责的分配,描述了对象和类的模式,以及它们之间的通信模式。 用来对类或对象怎样交互和怎样分配职责进行描述 1、 策略模式(strategy) 是指定义一系列的算法,把它们一个...
  • 设计模式 - 结构型设计模式 - 享元模式(Java)

    万次阅读 多人点赞 2019-02-11 11:38:45
    分享一个大牛的人工智能教程。零基础!通俗易懂!...每次需要一个对象的时候,先到 HashMap 中看看有没有,如果没有,再生成新的对象,然后将这个对象放入 HashMap 中。 这种简单的代码就不演示了。
  • 设计模式

    千次阅读 多人点赞 2019-05-15 20:52:02
    设计模式是在特定环境下为解决某一通用软件设计问题提供的一套定制的解决方案,该方案描述了对象和类之间的相互作用。 2.设计模式的基本要素 模式名称:模式名称通过一两个单词来描述模式的问题、解决方案和效果...
  • C#面向对象设计模式纵横谈(7):Adapter 适配器模式(结构型模式) C#面向对象设计模式纵横谈(8):Bridge 桥接模式(结构型模式) C#面向对象设计模式纵横谈(9):Composite 组合模式(结构型模式) C#面向对象设计模式...
  • 这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。这种模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类。这两种类型的类可被结构化改变而互不影响。...
  • 博主在大三的时候有上过...总体来说设计模式分为三大类:创建型模式、结构型模式和行为型模式。博主的上一篇文章已经提到过创建型模式,此外该文章还有设计模式概况和设计模式的六大原则。设计模式的六大原则是设计模式
  • 这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。举个真实的例子,读卡器是作为内存卡和笔记本之间的适配器。您将内存卡插入...
  • 1.装饰者模式死板的概念就不写了,怎么简单怎么来。 首先,从名字分析,顾名思义,装饰不就是给已有的东西额外增加一些功能或属性?。而且既然是要装饰某一个类,那么肯定要有装饰类(Police)和被装饰类(People)的...
  • 设计模式可以分为创建型、结构型、和行为型模式。 创建型模式对类的实现化过程进行了抽象,能够使软件模块做到与对象的创建和组织无关。 结构型模式描述类和对象之间如何进行有效的...行为型设计模式描述类和对...
  • Java面向对象设计模式

    万次阅读 2017-05-02 14:17:36
    设计模式(Design Patterns)  ——可复用面向对象软件的基础  设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易...
  • 过滤器模式(Filter Pattern...这种类型的设计模式属于结构型模式,它结合多个标准来获得单一标准。实现我们将创建一个Person对象、Criteria接口和实现了该接口的实体类,来过滤 Person 对象的列表。CriteriaPatter...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 260,253
精华内容 104,101
关键字:

对象结构型设计模式