精华内容
下载资源
问答
  • 适配器模式(Adapter):直观理解就是使原来不能一起相互工作(接口不兼容)的两个功能通过Adapter兼容在一起。 类适配器和对象适配器 类适配器 Adapter 类继承Adaptee (被适配类),同时实现Target 接口...

    什么是适配器模式?

    适配器模式(Adapter):直观理解就是使原来不能一起相互工作(接口不兼容)的两个功能通过Adapter兼容在一起。

    类适配器和对象适配器

    类适配器

    Adapter 类继承Adaptee (被适配类),同时实现Target 接口(因为 Java 不支持多继承,所以只能通过接口的方法来实现多继承),在 Client 类中我们可以根据需要选择并创建任一种符合需求的子类,来实现具体功能。

    对象适配器

    不使用多继承或继承的方式,而是使用直接关联,或者称为委托的方式。

    区别

    类适配器的重点在于类,是通过构造一个继承Adaptee类来实现适配器的功能;
    对象适配器的重点在于对象,是通过在直接包含Adaptee类来实现的,当需要调用特殊功能的时候直接使用Adapter中包含的那个Adaptee对象来调用特殊功能的方法即可。

    Demo

    类适配器

    // 已存在的、具有特殊功能、但不符合我们既有的标准接口的类
    class Adaptee {
        public void specificRequest() {
            System.out.println("被适配类 具有特殊功能...");
        }
    }
    
    // 目标接口,或称为标准接口
    interface Target {
        public void request();
    }
    
    // 具体目标类,只提供普通功能
    class ConcreteTarget implements Target {
        public void request() {
            System.out.println("普通类 具有普通功能...");
        }
    }
    
    // 适配器类,继承了被适配类,同时实现标准接口
    class Adapter extends Adaptee implements Target{
        public void request() {
            super.specificRequest();
        }
    }
    
    // 测试类
    public class Client {
        public static void main(String[] args) {
            // 使用普通功能类
            Target concreteTarget = new ConcreteTarget();//实例化一个普通类
            concreteTarget.request();
    
            // 使用特殊功能类,即适配类
            Target adapter = new Adapter();
            adapter.request();
        }
    }
    

    测试结果:
    普通类 具有普通功能…
    被适配类 具有特殊功能…

    对象适配器

    // 适配器类,直接关联被适配类,同时实现标准接口
    class Adapter implements Target{
        // 直接关联被适配类
        private Adaptee adaptee;
    
        // 可以通过构造函数传入具体需要适配的被适配类对象
        public Adapter (Adaptee adaptee) {
            this.adaptee = adaptee;
        }
    
        public void request() {
            // 这里是使用委托的方式完成特殊功能
            this.adaptee.specificRequest();
        }
    }
    
    // 测试类
    public class Client {
        public static void main(String[] args) {
            // 使用普通功能类
            Target concreteTarget = new ConcreteTarget();
            concreteTarget.request();
    
            // 使用特殊功能类,即适配类,
            // 需要先创建一个被适配类的对象作为参数
            Target adapter = new Adapter(new Adaptee());
            adapter.request();
        }
    }

    测试结果:
    普通类 具有普通功能…
    被适配类 具有特殊功能…

    展开全文
  • 对象结构型模式

    万次阅读 2019-11-27 21:55:46
    在适配器模式中可以定义一个包装类,包装不兼容接口的对象,这个包装类指的就是适配器(Adapter),它所包装的对象就是适配者 (Adaptee),即被适配的类。适配器提供客户类需要的接口,适配器的实现就是把客户类的请求 ...

    对象结构型模式

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

    展开全文
  • 【面向对象】——设计模式之结构型模式

    千次阅读 热门讨论 2014-12-28 11:27:18
    结构型模式包括:适配器、装饰、桥接、组合、享元、代理、外观模式。 适配器模式: 将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容不能一起工作的那些类可以一起工作。

    结构型模式包括:适配器、装饰、桥接、组合、享元、代理、外观模式。

    适配器模式:

    将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。



    什么时候用:使用一个已经存在的类,但如果它的接口,也就是它的方法和你的要求不相同时,就应该考虑用适配器模式。

    就比如:一个只会说英文的上司给只会中文的员工分配任务,由于语言问题,该员工根本听不懂。但给员工配备一个翻译,就没问题了。那这个翻译就相当于适配器。

    该例子的类图为:


    桥接模式:

    将抽象部分与它的现实部分分离,使它们都可以独立地变化。


    实现系统可能有多角度分类,每一张分类都有可能变化,那么就把这种多角度分离出来让它们独立变化,减少它们之间的耦合。

    就比如:手机与软件的关系,若这样:

    当有新软件产生时,需要在手机软件下添加新软件类,新软件类下还要加上所有手机品牌。该处只有两个品牌,若多的话,这种更改将是浩大的工程。

    若利用桥接模式:


    无论要增加手机品牌,还是软件,都只需更改相对应的类,不会对其他产生任何影响。该模式也很好的体现了合成/聚合复用原则。


    组合模式:

    将对象组合成树形结构以表示‘部分-整体’的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。


    什么时候用:当你发现需求中是体现部分与整体层次的结构时,以及你希望用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时,就应该考虑使用组合模式了。

    就比如:超市会员卡,该卡在总店可以刷,在下属分店也可以刷,在分店下属加盟店同样可以刷。并且人们不用考虑他在哪个店刷的,因为无论在哪刷卡,积分和折扣都是相同的。


    装饰模式:

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


    装饰模式是为已有功能动态的添加更多功能的一种方式。

    就比如:

    刚买一辆汽车如下图

    此汽车不符合你的个性要求,比如外表不够美观,发动机马力不足,不能满足你的速度激情,于是你需要对汽车的外表进行装饰,同时需要提高发动机的性能。这些操作通过装饰模式就能很好地解决问题。最终得到如下图所示的个性汽车。

    外观模式:

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


    什么时候用:首先,在设计初期阶段,应该要有意识的将不同的两个层分离,层与层之间建立外观Facade。其次,在开发阶段,子系统往往因为不断的重构演化而变得越来越复杂,这时,增加外观Facade可以提供一个简单的接口,减少它们之间的依赖。第三,在维护一个遗留的大型系统时,可能这个系统已经非常难以维护和扩展了,为新系统开发一个外观Facade类,来提供设计粗糙或高度复杂的遗留代码的比较清晰简单的接口,让新系统与Facade对象交互,Facade与遗留代码交互所有复杂的工作。

    就比如:开关。将复杂的内部结构增加一个外观,即按钮。当需要实现它的功能时,只需要点击按钮即可,不需要考虑内部啥样。


    享元模式:

    运用共享技术有效地支持大量细粒度的对象。


    什么时候用:如果一个应用程序使用了大量的对象,而大量的这些对象造成了很大的存储开销时就应该考虑使用;还有就是对象的大多数状态可以外部状态,如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象,此时可以考虑使用享元模式。

    就比如:公共交换电话网的使用。公共交换电话网中的一些资源,例如拨号音发生器、振铃发生器和拨号接收器,都是由所有用户共享的,因为不可能为每一个人都配备一套这样的资源,否则公共交换电话网的资源开销就太大了。当一个用户拿起听筒打电话时,他需要的就是拨打号码,拨通电话就行了。所以,所有人共用一套资源,非常节省资源,这就是享元模式的基本思想。


    代理模式:

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


    什么时候用:第一:远程代理,也就是为一个对象在不同的地址空间提供局部代表。这样可以隐藏一个对象存在于不同地址空间的事实。第二:虚拟代理,是根据需要创建开销很大的对象。通过它来存放实例化需要很长时间的真实对象。第三:安全代理,用来控制真实对象访问时的权限。第四:智能引导,是指当调用真实的对象时,打理处理另外一些事。

    就比如:


    结构型模式,顾名思义讨论的是类和对象的结构,它采用继承机制来组合接口或实现(类结构型模式),或者通过组合一些对象,从而实现新的功能(对象结构型模式)。

    总结对比:

    代理模式&&外观模式

    代理的客户对象无法直接访问目标对象,代理对象提供对单独目标对象的访问控制,而外观模式的客户对象可以直接访问子系统中的各个对象,但通常由外观对象提供对子系统个元件功能的简化的共同层次的调用接口。

     

    代理模式&&适配器模式

    二者都属于一种衔接性质的功能。代理对象和被代理对象的接口是同一个,但是客户没法直接访问被代理者,只能通过代理对象去完成被代理对象的访问。而适配器模式是将多个子系统封装起来,提供一个统一的外部接口,客户只需要使用这个外部接口即可访问对象的子系统了。

            

    外观模式&&适配器模式

    二者都是对系统的封装。外观模式定义了一个新的接口,而适配器则是复用了一个原有的接口;适配器是用来适配对象的,而外观则是用来适配整个子系统的。


    展开全文
  • 0导言  我们笔记本电脑的工作电压是20V,而家庭额定电压220...同样在软件开发中,有时也存在类似这种不兼容的情况,我们也可以像引入一个电源适配器一样引入一个称之为适配器的角色来协调这些存在不兼容的结构,这种设

    0导言

      我们笔记本电脑的工作电压是20V,而家庭额定电压220V,如何让20V的笔记本电脑能够在220V的电压下工作?为了解决这种问题,我们引入了一个电源适配器(AC Adapter),俗称充电器,有了这个电源适配器,生活用电和笔记本电脑即可兼容

    同样在软件开发中,有时也存在类似这种不兼容的情况,我们也可以像引入一个电源适配器一样引入一个称之为适配器的角色来协调这些存在不兼容的结构,这种设计方案即为适配器模式。

     

    1解释

     

    将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。——Gang of Four

    模式中的角色

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

      2 需要适配的类(Adaptee):需要适配的类或适配者类。

      3 适配器(Adapter):通过包装一个需要适配的对象,把原接口转换成目标接口。  

    1基本信息

    共有两类适配器模式:

    ①对象适配器模式

    在这种适配器模式中,适配器容纳一个它包裹的类的实例。在这种情况下,适配器调用被包裹对象的物理实体。

    ②类适配器模式

    这种适配器模式下,适配器继承自已实现的类(一般多重继承)。

     

    1)对象适配器:

    对象适配器”通过组合除了满足“用户期待接口”还降低了代码间的不良耦合。在工作中推荐使用“对象适配”。


    #include <iostream>
    
    // 目标类 -=> 对应于目标抽象类,内含我们现在项目需要的接口
    // 相当于20V的电压接口
    class Target
    {
    public :
        virtual ~Target( )
        {
    
        }
    
        virtual void Request( )            // 希望获得的系统接口
        {
            std::cout <<"Target::Request" <<std::endl;
        }
    };
    
    
    // 需要适配的类, 内含不符合要求的接口
    // 相当于已经提供好的220V的电压插座接口
    class Adaptee
    {
    public :
        void SpecialRequest( )
        {
            std::cout <<"Adaptee::SpecialRequest..." <<std::endl;
        }
    };
    
    
    // 获取接口的适配器类,
    // 适配后的符合要求的接口
    // 将目标类Adatee中不符合要求的接口, 转换为Target中的接口类型
    // 相当于一个转接器,将成员m_adaptee[Adaptee]的220V电压接口, 转换成Target类的20V电压接口
    class Adapter : public Target
    {
    public :
        Adapter( )
        :m_adaptee(new Adaptee)
        {
            //this->m_adaptee = new Adaptee( );
        }
    
        ~Adapter( )
        {
            if(this->m_adaptee != NULL)
            {
                delete this->m_adaptee;
                this->m_adaptee = NULL;
            }
        }
    
    
        void Request( )         // 我们需要的目标接口
        {
            // 将成员m_adaptee[Adaptee]的220V电压接口, 转换成Target类的20V电压接口
            this->m_adaptee->SpecialRequest( ); // 调用适配类的特殊(不符合)接口
        }
    
        Adaptee *m_adaptee;
    };
    
    
    int main(void)
    {
        Target *target = new Adapter( );            // 定义一个目标接口
        target->Request( );
    
    
        delete target;
        target = NULL;
    
    }
    

    2)类适配器:

    当客户在接口中定义了他期望的行为时,我们就可以应用适配器模式,提供一个实现该接口的类,并且扩展已有的类,通过创建子类来实现适配。


    #include <iostream>
    
    // 目标接口信息
    class Target
    {
    public :
        virtual void Request( )            // 希望获得的系统接口
        {
            std::cout <<"Target::Request" <<std::endl;
        }
    };
    
    
    // 需要适配的类, 内喊不符合要求的接口
    class Adaptee
    {
    public :
        void SpecialRequest( )
        {
            std::cout <<"Adaptee::SpecialRequest..." <<std::endl;
        }
    };
    
    
    // 获取接口的适配器类,
    // 适配后的符合要求的接口
    // 将目标类Adatee中不符合要求的接口, 转换为Target中的接口类型
    class Adapter : public Target, Adaptee
    {
    public :
        void Request( )
        {
            Adaptee::SpecialRequest( );
        }
    };
    
    
    int main()
    {
        Target *target = new Adapter( );
        target->Request( );
    
    
        delete target;
        target = NULL;
    
        return 0;
    }
    
    


    展开全文
  • JavaScript面向对象编程之创建对象

    千次阅读 2016-09-23 18:46:06
    背景 工厂模式 构造函数模式 构造函数模式的具体使用 将构造函数当做普通函数使用 ...稳妥构造函数模式背景我们使用Object构造函数或对象字面量来创建单个对象。但是如果我们使用同一个接口创建很多对象的话,会产生
  • 适配器模式(Adapter Pattern):将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。 注意:这里的...
  • 不兼容结构的协调——适配器模式(四)

    万次阅读 多人点赞 2013-03-01 09:23:35
    缺省适配器模式的定义如下:缺省适配器模式(Default Adapter Pattern):当需要实现一个接口所提供的所有方法时,可先设计一个抽象类实现该接口,并为接口中每个方法提供一个默认实现(空方法),那么该抽象类的...
  • 面向对象的23种设计模式

    千次阅读 2017-08-16 15:42:34
    首先呢,设计模式是针对面向对象来的。再说设计模式之前我们先来说一下六大设计原则。 首先最基础的开闭原则:对扩展开放,对修改关闭。 为啥这是最基础的,因为我们都知道软件是要改的。对扩展开放保证了可以增加...
  • 不兼容结构的协调——适配器模式(二)

    万次阅读 多人点赞 2013-03-01 00:31:43
    9.3 完整解决方案 Sunny软件公司开发人员决定使用适配器模式来重用算法库中的算法,其基本结构如图9-4所示:图9-4 算法库重用结构图 在图9-4中,ScoreOperation接口充当抽象目标,QuickSort和BinarySearch类充当...
  • 【面向对象】设计模式总结

    千次阅读 热门讨论 2016-11-11 10:07:11
    一、前言 设计模式是软件编程提升水平的一个重要技能,而且在软件攻城狮中考试里,也是比可少的,所以小编总结了历年的软考真题,总结了设计模式。希望可以给您带来帮助。二、行为型行为型一共分为5种,记忆方式...
  • 本文介绍了设计模式的基本分类、通过案例的方式介绍面向对象的 6 大设计原则,最后通过项目案例分析如何使用面向对象的设计原则
  • 单例模式是一个类在系统中只有一个实例对象。通过全局的一个入口点对这个实例对象进行访问。 在iOS开发中,单例模式是非常有用的一种设计模式。 OS SDK中也有许多类使用了单例模式,例如,UIApplication:当程序...
  • Adapter 模式使得原本由于接口不兼容不能一起工作的那些类可以一起工作。 适用性: 你想使用一个已经存在的类,而它的接口不符合你的需求。 你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类...
  • 使用通用模块定义 (UMD) 模式创建一个库或模块,以提供与当今最流行的脚本加载器的兼容性。 支持的模块 CommonJS - 、 、 等。 AMD - 、 等。 全局变量 - 添加到 Web 浏览器中的窗口对象。 执照 版权所有 (c) ...
  • 深入理解js面向对象——创建对象

    千次阅读 2017-07-04 15:50:11
    本博客原文在这 由于js这门语言是解析型语言的特性,导致js中的对象更加灵活,更像是一些 属性的集合,或者说类似于散... js创建对象有几种方式? 他们的内部执行过程是什么? 通过这篇文章,可以一探究竟.思维导图如下: #
  • 面向对象——设计模式

    千次阅读 2019-04-08 13:39:59
    一、设计模式的要素 模式名称(Pattern Name):一个助记符,它用一两个词来描述模式的问题、解决方案和效果。 问题(Problem):问题描述了应该在何时使用模式。...二、创建型设计模式 创建型...
  • 设计模式 - 创建型设计模式小结

    万次阅读 2019-03-23 16:31:11
    单例模式说了,为了保证全局使用的是同一对象,一方面是安全性考虑,一方面是为了节省资源;建造者模式专门对付属性很多的那种类,为了让代码更优美;原型模式用得最少,了解和 Object 类中的 clone() 方法相关...
  • 设计模式 | 适配器模式及典型应用

    万次阅读 多人点赞 2018-09-20 01:37:29
    适配器模式(Adapter Pattern):将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。 在适配器模式...
  • 面向对象的设计模式

    千次阅读 2012-01-05 10:53:57
    设计模式模式是一种问题的解决思路,它已经适用于一个实践环境,并且可以适用于其他环境。 设计模式的分类:分布式编程模式,用户界面模式,数据模型模式三大类。 设计模式的作用:  设计的重用;  为...
  • 前言设计模式(design pattern)是一套被反复使用、多数人知晓、经过分类编目的优秀代码设计经验的总结。关键词:重用代码、工程化、面向对象。设计模式起源于建筑设计学,最先由 Gang of Four 提升到了理论高度。
  • Node.js:设计模式 13 种最流行的面向对象设计模式应用于 Node.js。... 适配器让那些因为接口不兼容不能一起工作的类一起工作。 代理模式 为另一个对象提供代理或占位符以控制对其的访问。 复合图案 将
  • 文章目录设计模式GoF设计模式简要说明创建模式简单工厂模式工厂方法模式应用实例模式优缺点适用场景抽象工厂模式模式优缺点结构型模式行为型模式 设计模式 设计模式是在特定环境下为解决某一通用软件设计问题...
  • A d a p t e r 模式使得原本由于接口不兼容不能一起工作的那些类可以一起工作。 适用场景: 1、已经存在的类的接口不符合我们的需求; 2、创建一个可以复用的类,使得该类可以与其他不相关
  • 怎么难道开发IE7的"新警察"知道IE6们都用ActiveX对象XmlHttp吗?XmlHttp出了什么问题,IE7为什么要这么做?原来一切就为了一个简单的兼容而已,但让人感慨颇多。 IE7提供XMLHttpRequest对象后,当然会继续支持...
  • 1.1什么是设计模式 问题一:什么是设计模式? 提供相关问题的解决方案 问题二:一个模式的基本要素是什么? 模式名称(pattern name) 问题(problem) 解决方案(solution) 效果(consequences) 1.2smalltalk ...
  • Adapter 模式使得原本由于接口不兼容不能一起工作的那些类可以一起工作。 2.别名 包装器Wrapper。 3.适用性 1)你想使用一个已经存在的类,而它的接口不符合你的需求。 2) 你想创建一个可以复用的类,该类可以与...
  • 浏览器兼容性改造之创建兼容性XHR

    千次阅读 2012-10-11 15:20:28
    在IE5中,XMLHttpRequest对象是通过MSXML库中的一个ActiveX对象实现的。因此,在IE中可能遇到3中不同版本的XMLHttpRequest对象,即MSXML2.XMLHttp、MSXML2.XMLHttp.3.0和MSXML2.XMLHttp.6.0。要使用MSXML库中的...
  • 创建对象的几种方式

    千次阅读 2017-04-11 18:25:20
    在javascript中,所有对象创建都是基于原型的。在js中任意的对象都有一个内部属性[[Prototype]]。这个属性的值只能是object或者是null。对象有这个内部属性的目的就是为了实现继承,或者更明确的说实现属性(方法...
  • 建造者模式是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户需要知道内部的具体构建细节。建造者模式属于对象创建模式。根据中文翻译的不同,建造者模式又可以称为...
  • 遇到此问题时搜索知道,发现答案固定,但是大多数都是此方案解决,特此记录 情景:Win7 +VirtualBox (VBox) 4.3.8-92456 ...修改 属性 --> 兼容性 --> 以兼容模式运行这个程序--> WIndows Server 20

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 194,779
精华内容 77,911
关键字:

兼容模式不能创建对象