设计模式笔记

UML

http://www.uml.org.cn/oobject/201211231.asp

Iterator模式

迭代器模式——一个一个遍历

在for循环中,将i的作用抽象化、通用化后形成的模式,在设计模式中称为Iterator模式。

Iterator模式用于在数据集合中按照顺序遍历集合。

Iterator模式类图

登场角色

  • Iterator(迭代器):该角色负责按顺序逐个遍历元素的接口(API)。
  • ConcreteIterator(具体的迭代器):该角色负责实现Iterator角色所定义的接口(API)。
  • Aggregate(集合):该角色负责定义创建Iterator角色的接口(API)。
  • ConcreteAggregate(具体的集合):该角色负责实现Aggregate角色所定义的接口(API)。

代码

public interface Aggregate<T> {
    Iterator<T> iterator();
}
public class BookShelf implements Aggregate<Book> {
    private List<Book> books = new ArrayList<>();
    private Integer last = 0;

    public Book getBookAt(Integer index) {
        return books.get(index);
    }

    public void appendBook(Book book) {
        books.add(book);
        last++;
    }

    public Integer getLength(){
        return last;
    }

    @Override
    public Iterator<Book> iterator() {
        return new BookShelfIterator(this);
    }
}
public class BookShelfIterator implements Iterator<Book> {
    private BookShelf bookShelf;
    private Integer index;

    public BookShelfIterator(BookShelf bookShelf) {
        this.bookShelf = bookShelf;
        index = 0;
    }

    @Override
    public boolean hasNext() {
        Integer last = bookShelf.getLength();
        return index < last;
    }

    @Override
    public Book next() {
        return bookShelf.getBookAt(index++);
    }
}
@Data
public class Book {
    private String name;
}

使用原因

使用Iterator的hasNext()和next()方法,没有调用实体类的方法,循环遍历不依赖于类的实现。

相关的设计模式

  • Visitor模式:Iterator模式是从集合中一个一个取出元素进行遍历,但是并没有在Iterator接口中声明对取出的元素进行何种处理。Visitor模式则是在遍历元素集合的过程中,对元素进行相同的处理。在遍历集合的过程中对元素进行固定的处理是常用的需求。Visitor模式正是为了应对这种需求而出现的。在访问元素集合的过程中对元素进行相同的处理,这种模式就是Visitor模式。
  • Composite模式:Composite模式是具有递归结构的模式,在其中使用Iterator模式比较困难。
  • Factory Method模式:在iterator方法中生成Iterator的实例时可能会使用Factory Method模式。

Adapter模式

适配器模式——加个“适配器”以便于复用

在程序中,经常会存在现有的程序无法直接使用,需要做适当的变换之后才能使用的情况。这种用于填补“现有的程序”和“所需的程序”之间差异的设计模式就是Adapter模式

Adapter模式也被称为Wrapper模式。Wrapper有“包装器”的意思,就像用精美的包装纸将普通商品包装成礼物那样,替我们把某样东西包起来,使其能够用于其他用途的东西就被称为“包装器”或是“适配器”。

Adapter模式有以下两种:

  • 类适配器模式(使用继承的适配器)
  • 对象适配器模式(使用委托的适配器)

登场角色

  • Target(对象):该角色负责定义所需的方法。
  • Client(请求者):该角色负责使用Target角色所定义的方法进行具体处理。
  • Adaptee(被适配):持有既定方法的角色。
  • Adapter(适配):使用Adaptee角色的方法来满足Target角色的需求,这是Adapter模式的目的,也是Adapter角色的作用。

使用场景

Adapter模式对现有的类进行适配,生成新的类。通过该模式可以很方便地创建需要的方法群。当出现bug时,由于我们很明确地知道bug不在现有类(Adaptee角色中),所以只需调查Adapter即可,代码的排查就会变得非常简单。

使用Adapter模式可以在完全不改变现有代码的前提下使现有代码适配于新的接口(API)。此外,在Adapter模式中,并非一定需要现成的代码。只要知道现有类的功能,就可以编写出新的类。

相关的设计模式

  • Bridge模式:Adapter模式用于连接接口(API)不同的类,而Bridge模式则用于连接类的功能层次结构与实现层次结构。
  • Decorator模式:Adapter模式用于填补不同接口(API)之间的缝隙,而Decorator模式则是在不改变接口(API)的前提下增加功能。

类适配器模式

类适配模式

代码

public interface Print {
    void printWeak();

    void printStrong();
}
public class Banner {
    private String str;

    public Banner(String str) {
        this.str = str;
    }

    /**
     * 用括号包围
     */
    public void showWithParen() {
        System.out.println("(" + str + ")");
    }

    /**
     * 用星号包围
     */
    public void showWithAster() {
        System.out.println("*" + str + "*");
    }
}
public class PrintBanner extends Banner implements Print {
    public PrintBanner(String str) {
        super(str);
    }

    @Override
    public void printWeak() {
        showWithParen();
    }

    @Override
    public void printStrong() {
        showWithAster();
    }
}

对象适配器模式

对象适配模式

代码

public abstract class Print {
    public abstract void printWeak();

    public abstract void printStrong();
}
public class Banner {
    private String str;

    public Banner(String str) {
        this.str = str;
    }

    /**
     * 用括号包围
     */
    public void showWithParen() {
        System.out.println("(" + str + ")");
    }

    /**
     * 用星号包围
     */
    public void showWithAster() {
        System.out.println("*" + str + "*");
    }
}
public class PrintBanner extends Print {
    private Banner banner;

    public PrintBanner(String str) {
        this.banner = new Banner(str);
    }

    @Override
    public void printWeak() {
        banner.showWithParen();
    }

    @Override
    public void printStrong() {
        banner.showWithAster();
    }
}

Template Method模式

模板方法模式——将具体处理交给子类

带有模板功能的模式,组成模板的方法被定义在父类中。由于这些方法是抽象方法,所以只查看父类的代码是无法知道这些方法最终会进行何种具体处理的,唯一能知道的就是父类是如何调用这些方法的。

实现上述这些抽象方法的是子类。在子类中实现了抽象方法也就决定了具体的处理。也就是说,只要在不同的子类中实现不同的具体处理,当父类的模板方法被调用时程序的行为也会不同。但是,不论子类中的具体实现如何,处理的流程都会按照父类中所定义的那样进行。

向这样在父类中定义处理流程的框架,在子类中实现具体处理的模式就称为Template Method模式。

在父类中定义处理的流程,在子类中实现具体处理内容。

登场角色

  • AbtractClass(抽象类):AbstractClass角色不仅负责实现模板方法,还负责声明在模板方法中所使用到的抽象方法。这些抽象方法由子类ConcreteClass角色负责实现。
  • ConcreteClass(具体类):该角色负责具体实现AbstractClass角色中定义的抽象方法。

Template Method模式

注意

由于在父类的模板方法中编写了算法,因此无需在每个子类中再编写算法。

Template Method模式中,父类和子类是紧密联系、共同工作的。因此,在子类中实现父类中声明的抽象方法时,必须要理解这些抽象方法被调用的时机。在看不到父类源码的情况下,想要编写出子类是非常困难的。

使用父类类型的变量保存子类实例的优点是,即使没有用instanceof等指定子类的种类,程序也能正常工作。

无论在父类类型的变量中保存哪个子类的实例,程序都可以正常工作,这种原则称为里氏替换原则(LSP)。LSP并非仅限于Template Method模式。它是通用的继承原则。

相关的设计模式

  • Factory Method模式:是将Template Method模式用于生成实例的一个典型例子。
  • Strategy模式:在Template Method模式中,可以使用继承改变程序的行为。这是因为Template Method模式在父类中定义程序行为的框架,在子类中决定具体的处理。于此相对的是Strategy模式,它可以使用委托改变程序的行为。与Template Method模式中改变部分程序行为不同的是,Strategy模式用于替换整个算法。

代码

public abstract class AbstractDisplay {
    public abstract void open();

    public abstract void print();

    public abstract void close();

    public final void display() {
        open();
        for (int i = 0; i < 5; i++) {
            print();
        }
        close();
    }
}
public class CharDisplay extends AbstractDisplay {
    private char ch;

    public CharDisplay(char ch) {
        this.ch = ch;
    }

    @Override
    public void open() {
        System.out.print("<<");
    }

    @Override
    public void print() {
        System.out.print(ch);
    }

    @Override
    public void close() {
        System.out.println(">>");
    }
}
public class StringDisplay extends AbstractDisplay {
    private String str;
    private Integer width;

    public StringDisplay(String str) {
        this.str = str;
        width = str.getBytes().length;
    }

    @Override
    public void open() {
        printLine();
    }

    @Override
    public void print() {
        System.out.println("|" + str + "|");
    }

    @Override
    public void close() {
        printLine();
    }

    private void printLine() {
        System.out.print("+");
        for (int i = 0; i < width; i++) {
            System.out.print("-");
        }
        System.out.println("+");
    }
}

Factory Method模式

工厂方法模式——将实例的生成交给子类

Template Method模式中,我们在父类中规定处理的流程,在子类中实现具体的处理,如果将该模式用于生成实例,就是Factory Method模式。

Template Method模式来构建生成实例的工厂,这就是Factory Method模式。

Factory Method模式中,父类决定实例的生成方式,但并不决定所要生成的具体的类,具体的处理全部交给子类负责。这样就可以将生成实例的框架(framework)和实际负责生成实例的类解耦。

登场角色

工厂方法模式

  • product(产品):属于框架方,是一个抽象类。定义了在Factory Method模式中生成的那些实例所持有的接口(API),但具体的处理则由子类ConcreteProduct角色决定。
  • Creator(创建者):属于框架方,负责生成Product角色的抽象类,但具体的处理则由子类ConcreteCreator角色决定。Creator对实际负责生成实例的ConcreteCreator一无所知,唯一知道的是,只要调用Product 角色和生成实例的方法,就可以生成Product的实例。不用new关键字来生成实例,而是调用生成实例的专用方法来生成实例,这样就可以防止父类与其他具体类耦合。
  • ConcreteProduct(具体的产品):属于具体加工方,决定了具体的产品。
  • ConcreteCreator(具体的创建者):属于具体加工方,负责生成具体的产品。

生成实例——三种实现

  • 指定其为抽象方法:一旦将createProduct指定为抽象方法后,子类就必须实现该方法。如果子类不实现该方法,编译器将会报告编译错误。
  • 为其实现默认处理:这时使用new关键字创建出实例,因此不能将Product类定义为抽象类。
  • 在其中抛出异常:createProduct方法的默认处理为抛出异常,这样一来,如果未在子类中实现该方法,程序就会运行时报错,不过需要另外编写FactoryMethodRuntimeException异常类。

代码

framework包

public abstract class Product {
    public abstract void use();
}
public abstract class Factory {
    public final Product create(String owner) {
        Product product = createProduct(owner);
        registerProduct(product);
        return product;
    }

    protected abstract Product createProduct(String owner);

    protected abstract void registerProduct(Product product);
}

idcard包

public class IdCard extends Product {
    @Getter
    private String owner;

    IdCard(String owner) {
        this.owner = owner;
    }

    @Override
    public void use() {
        System.out.println("owner:" + owner);
    }
}
public class IdCardFactory extends Factory {
    @Getter
    private List<String> owners = new ArrayList<>();

    @Override
    protected Product createProduct(String owner) {
        return new IdCard(owner);
    }

    @Override
    protected void registerProduct(Product product) {
        owners.add(((IdCard) product).getOwner());
    }
}

相关的设计模式

  • Template Method模式:Factory Method模式是Template Method的典型应用。
  • Singleton模式:在多数情况下我们可以将Singleton模式用于扮演Creator角色(或是ConcreteCreator角色)的类。这是因为在程序中没有必要存在多个Creator角色(或是ConcreteCreator角色)的实例。
  • Composite模式:有时可以将Composite模式用于Product角色(或是ConcreteProduct角色)。
  • Iterator模式:在Iterator模式中使用iterator方法生成Iterator的实例时会使用Factory Method模式。

Singleton模式

单例模式——只有一个实例

确保只生成一个实例的模式被称为Singleton模式。Singleton是指只含有一个元素的集合。

登场角色

单例模式类图

饿汉式

/**
 * 饿汉式
 */
public class HungrySingleton {
    private static HungrySingleton instance = new HungrySingleton();

    private HungrySingleton() {
        System.out.println("生成了一个实例");
    }

    public static HungrySingleton getInstance() {
        return instance;
    }
}

懒汉式

public class LazySingleton {
    private static LazySingleton instance = null;
    private static final ReentrantLock lock = new ReentrantLock();

    private LazySingleton() {
        System.out.println("生成了一个实例");
    }

    public static LazySingleton getInstance() {
        if (instance == null) {
            lock.lock();
            try {
                if (instance == null) {
                    instance = new LazySingleton();
                }
            } finally {
                lock.unlock();
            }
        }
        return instance;
    }
}

相关的设计模式

  • AbstractFactory模式
  • Bulder模式
  • Facade模式
  • Prototype模式

Prototype模式

原型模式——通过复制生成实例

在以下情况下,我们就不能根据类来生成实例,而要根据现有的实例来生成新的实例:

  • 对象种类繁多,无法将它们整合到一个类中
  • 难以根据类生成实例
  • 想解耦框架与生成的实例时

代码

/**
 * 复制功能的接口
 */
public interface Product extends Cloneable {
    void use(String s);

    Product createClone();
}

/**
 * 使用Product接口来复制实例
 */
public class Manager {
    private Map<String, Product> showCase = new HashMap<>();

    public void register(String name, Product proto) {
        showCase.put(name, proto);
    }

    public Product create(String name) {
        Product product = showCase.get(name);
        return product.createClone();
    }
}
public class MessageBox implements Product {
    private char decoChar;

    public MessageBox(char decoChar) {
        this.decoChar = decoChar;
    }

    @Override
    public void use(String s) {
        int length = s.getBytes().length;
        for (int i = 0; i < length + 4; i++) {
            System.out.print(decoChar);
        }
        System.out.println();
        System.out.println(decoChar + " " + s + " " + decoChar);
        for (int i = 0; i < length + 4; i++) {
            System.out.print(decoChar);
        }
        System.out.println();
    }

    @Override
    public Product createClone() {
        Product clone = null;
        try {
            clone = (Product) clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return clone;
    }
}
public class UnderlinePen implements Product {
    private char ulChar;

    public UnderlinePen(char ulChar) {
        this.ulChar = ulChar;
    }

    @Override
    public void use(String s) {
        int length = s.getBytes().length;
        System.out.println("\"" + s + "\"");
        for (int i = 0; i < length; i++) {
            System.out.print(ulChar);
        }
        System.out.println();
    }

    @Override
    public Product createClone() {
        Product clone = null;
        try {
            clone = (Product) clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return clone;
    }
}

登场角色

原型模式类图

  • Prototype(原型):负责定义用于复制现有实例来生成新实例的方法。
  • ConcretePrototype(具体的原型):负责实现复制现有实例并生成新实例的方法。
  • Client(使用者):负责使用复制实例的方法生成新的实例。

相关的设计模式

  • Flyweight模式:使用Prototype模式可以生成一个与当前实例的状态完全相同的实例。而使用Flyweight模式可以在不同的地方使用同一个实例。
  • Memento模式:使用Prototype模式可以生成一个与当前实例的状态完全相同的实例。而使用Memento模式可以保存当前实例的状态,以实现快照和撤销功能。
  • Composite模式以及Decorator模式:经常使用Composite'模式和Decorator模式时,需要能够动态地创建复杂结构的实例。这时可以使用Prototype模式。
  • Command模式:想要复制Command模式中出现的命令时,可以使用Prototype模式。

注意

clone方法进行的是浅复制

clone方法所进行的复制只是将被复制实例的字段值直接复制到新的实例中。换言之,它并没有考虑字段中所保存的实例的内容。例如,当字段中保存的是数组时,如果使用clone方法进行复制,则只会复制该数组的引用,并不会一一复制数组中的元素。

像上面这样的字段对字段的复制(field-to-field-copy)被称为浅复制(shallow copy)。clone方法所进行的复制就是浅复制。

当使用clone方法进行浅复制无法满足需求时,类的设计者可以实现重写clone方法,实现自己需要的复制功能(重写clone方法时,别忘了使用super.clone()来调用父类的clone方法)。

需要注意的是,clone方法只会进行复制,并不会调用被复制实例的构造函数。此外,对于在生成实例时需要进行特殊的初始化处理的类,需要自己去实现clone方法,在其内部进行这些初始化处理。

Builder模式

Abstract Factory模式

抽象工厂模式——将关键零件组装成产品

将抽象零件组合成为抽象产品的工厂。

代码

factory包

/**
 * 抽象的零件:Item
 */
public abstract class Item {
    /**
     * 标题
     */
    protected String caption;

    public Item(String caption) {
        this.caption = caption;
    }
    public abstract String makeHTML();
}
/**
 * 抽象的零件:Link
 */
public abstract class Link extends Item {
    protected String url;

    public Link(String caption, String url) {
        super(caption);
        this.url = url;
    }
}
/**
 * 抽象的零件:Tray
 * 表示一个含有多个Link和Tray类的容器
 */
public abstract class Tray extends Item {
    protected ArrayList<Item> tray = new ArrayList();

    public Tray(String caption) {
        super(caption);
    }

    public void add(Item item) {
        tray.add(item);
    }
}
/**
 * 抽象的产品:Page
 * 抽象地表示HTML页面的类
 */
public abstract class Page {
    protected String title;
    protected String author;
    protected ArrayList<Item> content = new ArrayList();

    public Page(String title, String author) {
        this.title = title;
        this.author = author;
    }

    public void add(Item item) {
        content.add(item);
    }

    public void output() {
        try {
            String filename = title + ".html";
            Writer writer = new FileWriter(filename);
            writer.write(makeHTML());
            writer.close();
            System.out.println(filename + "编写完成!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public abstract String makeHTML();
}
/**
 * 抽象的工厂:Factory
 */
public abstract class Factory {
    public static Factory getFactory(String classname) {
        Factory factory = null;
        try {
            factory = (Factory) Class.forName(classname).newInstance();
        } catch (ClassNotFoundException e) {
            System.out.println("没有找到" + classname + "类");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return factory;
    }

    public abstract Link createLink(String caption, String url);

    public abstract Tray createTray(String caption);

    public abstract Page createPage(String title, String author);
}

listfactory

/**
 * 具体的零件
 */
public class ListLink extends Link {
    public ListLink(String caption, String url) {
        super(caption, url);
    }

    @Override
    public String makeHTML() {
        return "<li><a href=\"" + url + "\">" + caption + "</a></li>";
    }
}
/**
 * 具体的零件
 */
public class ListTray extends Tray {
    public ListTray(String caption) {
        super(caption);
    }

    @Override
    public String makeHTML() {
        StringBuffer buffer = new StringBuffer();
        buffer.append("<li>");
        buffer.append(caption);
        buffer.append("<ul>");
        Iterator<Item> iterator = tray.iterator();
        while (iterator.hasNext()) {
            Item item = iterator.next();
            buffer.append(item.makeHTML());
        }
        buffer.append("</ul>");
        buffer.append("</li>");
        return buffer.toString();
    }
}
/**
 * 具体的产品
 */
public class ListPage extends Page {
    public ListPage(String title, String author) {
        super(title, author);
    }

    @Override
    public String makeHTML() {
        StringBuffer buffer = new StringBuffer();
        buffer.append("<html><head><title>" + title + "</title></head>");
        buffer.append("<body>");
        buffer.append("<h1>" + title + "</h1>");
        buffer.append("<ul>");
        Iterator<Item> iterator = content.iterator();
        while (iterator.hasNext()) {
            Item item = iterator.next();
            buffer.append(item.makeHTML());
        }
        buffer.append("</ul>");
        buffer.append("<hr><address>" + author + "</address>");
        buffer.append("</body>");
        buffer.append("</html>");
        return buffer.toString();
    }
}
/**
 * 具体的工厂
 */
public class ListFactory extends Factory {
    @Override
    public Link createLink(String caption, String url) {
        return new ListLink(caption, url);
    }

    @Override
    public Tray createTray(String caption) {
        return new ListTray(caption);
    }

    @Override
    public Page createPage(String title, String author) {
        return new ListPage(title, author);
    }
}

登场角色

抽象工厂

  • AbstractProduct(抽象产品):负责定义AbstractFactory角色所生成的抽象零件和产品的接口。
  • AbstractFactory(抽象工厂):负责定义用于生成抽象产品的接口(API)。
  • Client(委托者):仅会调用AbstractFactory角色和AbstractProduct角色的接口(API)来进行工作,对于具体的零件、产品和工厂一无所知。
  • ConcreteProduct(具体产品):负责实现AbstractProduct角色的接口(API)。
  • ConcreteFactory(具体工厂):负责实现AbstractFactory角色的接口(API)。

相关的设计模式

  • Builer模式:Abstract Factory模式通过调用抽象产品的接口(API)来组装抽象产品,生成具有复杂结构的实例。Builder模式则是分阶段地制作复杂实例。
  • Factory Method模式:有时Abstract Factory模式中零件和产品的生成会使用到Factory Method模式。
  • Composite模式:有时Abstract Factory模式在制作产品时会使用Composite模式。
  • Singleton模式:有时Abstract Factory模式中的具体工厂会使用Singleton模式。

Bridge模式

Strategy模式

Composite模式

Decorator模式

Visitor模式

Chain of Responsibility模式

Facade模式

Mediator模式

Observer模式

Memento模式

State模式

Flyweight模式

Proxy模式

代理模式——只在必要时生成实例

代码

代码时序

public interface Printable {
    void setPrinterName(String name);// 设置名字

    String getPrinterName();// 获取名字

    void print(String str);// 显示文字(打印输出)
}
public class Printer implements Printable {
    private String name;

    public Printer() {
        heavyJob("正在生成Printer实例");
    }

    public Printer(String name) {
        this.name = name;
        heavyJob("正在生成Printer实例(" + name + ")");
    }

    @Override
    public void setPrinterName(String name) {
        this.name = name;
    }

    @Override
    public String getPrinterName() {
        return name;
    }

    @Override
    public void print(String str) {
        System.out.println("===" + name + "===");
        System.out.println(str);
    }

    private void heavyJob(String msg) {
        System.out.print(msg);
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.print(".");
        }
        System.out.println("结束");
    }
}
public class PrinterProxy implements Printable {
    private String name;
    private Printer real;

    public PrinterProxy() {
    }

    public PrinterProxy(String name) {
        this.name = name;
    }

    @Override
    public synchronized void setPrinterName(String name) {
        if (real != null) {
            real.setPrinterName(name);
        }
        this.name = name;
    }

    @Override
    public String getPrinterName() {
        return name;
    }

    @Override
    public void print(String str) {
        realize();
        real.print(str);
    }

    private synchronized void realize() {
        if (real == null)
            real = new Printer(name);
    }
}

登场角色

代理模式类图

  • Subject(主体):定义了使ProxyRealSubject角色之间具有一致性的接口。由于存在Subject角色,所以Client角色不必在意它所使用的究竟是proxy还是RealSubject
  • Proxy(代理人):Proxy角色会尽量处理来自Client角色的请求。只有当自己不能处理时,它才会将工作交给RealSubject角色。Proxy角色只有在必要时才会生成RealSubject角色。Proxy角色实现了在Subject角色中定义的接口(API)。
  • RealSubject(实际的主体):“本人”RealSubject角色会在“代理人”Proxy角色无法胜任工作时出场。它与Proxy角色一样,也实现了在Subject角色中定义的接口(API)。
  • Client(请求者):使用Proxy模式的角色。

各种Proxy模式

  • Virtual Proxy(虚拟代理):只有当真正需要实例时,才生成和初始化实例。
  • Remote Proxy(远程代理):Remote Proxy可以让我们完全不必在意RealSubject角色是否在远程网络上,可以如同它在自己身边一样(透明性地)调用它的方法。Java的RMI(RemoteMethodInvocation:远程方法调用)就相当于Remote Proxy
  • Access ProxyAccess Proxy用于在调用RealSubject角色的功能时设置访问限制。例如,这种代理可以只允许指定的用户调用方法,而当其他用户调用方法时则报错。

相关的设计模式

  • Adapter模式:Adapter模式适配了两种具有不同接口(API)的对象,以使它们可以一同工作。而在Proxy模式中,Proxy角色与RealSubject角色的接口(API)是相同的(透明性)。
  • Decorator模式:Decorator模式与Proxy模式在实现上很相似,不过它们的使用目的不同。Decorator模式的目的在于增加新的功能。而在Proxy模式中,与增加功能相比,它更注重通过设置代理人的方式来减轻本人的工作负担。

为什么要使用代理模式

  • 中介隔离作用:在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。
  • 开闭原则,增加功能:代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要在修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是通过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要修改已经封装好的委托类。

有哪几种代理模式

如果按照代理创建的时期来进行分类的话,可以分为两种:静态代理、动态代理。

  • 静态代理是由程序员创建或特定工具自动生成源代码,再对其编译。在程序员运行之前,代理类.class文件就已经被创建了。
  • 动态代理是在程序运行时通过反射机制动态创建的。

JDK动态代理

在动态代理中我们不再需要再手动创建代理类,只需要编写一个动态处理器就可以了。真正的代理对象由JDK在运行时为我们动态创建。

第一步:创建服务类接口

第二步:实现服务接口

第三步:编写动态处理器

public class DynamicProxyHandler implements InvocationHandler {
    private Object object;

    public DynamicProxyHandler(final Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(object, args);
        return result;
    }
}

第四步:编写测试类

public static void main(String[] args) {
    Printer printer = new Printer();
    Printable proxy = (Printable) Proxy.newProxyInstance(Printable.class.getClassLoader(),
            new Class[]{Printable.class},
            new DynamicProxyHandler(printer));
    proxy.setPrinterName("打印机");
    proxy.print("Hello World");
}

CGLib动态代理(CGLib Proxy)

JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,就需要CGLib了。CGLib采用了底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以不能对final修饰的类进行代理。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。

CGLib子类代理实现方法:

  • 引入cglib的jar文件,asm的jar文件
  • 代理的类不能为final
  • 目标业务对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法。

第一步:创建服务类

第二步:创建CGLIB代理类

第三步:创建测试类

Command模式

Interpreter模式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值