适应设计模式

适应设计模式并非具有真正的字面含义:只是根据作者的用意,用最容易理解的设计模式,为我们入门
这里给出了其中两种设计模式:Iterator & Adapter

一、Iterator 模式

正如我在 设计模式前言中所述,java.util.Iterator 下面有实现,但是我们要做的是摒弃已经有的实现,自己从头理解设计这个模式的含义以及作用。

1.分析需求背景

程序为了实现将 书 (Book) 放置到书架 (BookShelf) 中,并将书的名字按照顺序 (书架中存放的顺序) 展示出来

2.UML

uml.png

3.Code

3.1 角色:Iterator 迭代器

/**
 * 该角色负责定义按顺序逐个遍历元素的 接口(API)
 * @author CSZ
 */
public interface Iterator {

    /**
     * @return 用于判断是否存在下一个
     */
    public abstract boolean hasNext();

    /**
     * @return 下一个元素
     */
    public abstract Object next();
}

3.2 角色:ConcreteIterator 具体迭代器

/**
 * 这里我们实现接口,自然会重写两个方法:
 * 该角色负责实现 Iterator 角色定义的接口(API)
 * @author CSZ
 */
public class BookShelfIterator implements Iterator{

    private final BookShelf bookShelf;
    private int index;

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

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

    @Override
    public Object next() {
        Book book = bookShelf.getBookAt(index);
        index ++;
        return book;
    }
}

3.3 角色:Aggregate 集合

/**
 * 角色:Aggregate 集合
 * 该角色负责定义创建 Iterator 角色的接口(API)
 * 接口(API) 是一个方法:创建一个迭代器
 * @author CSZ
 */
public interface Aggregate {

    /**
     * @return 返回一个 Iterator 接口
     */
    public abstract Iterator iterator();
}

3.4 角色:ConcreteAggregate 具体集合

/**
 * 通过实现 Aggregate 来重写 iterator() 来获取具体的迭代器
 * @author CSZ
 */
public class BookShelf implements Aggregate{

    private final Book[] books;

    private int last = 0;

    public BookShelf(int maxSize){
        this.books = new Book[maxSize];
    }

    public Book getBookAt(int index){
        return books[index];
    }

    public void appendBook(Book book){
        this.books[last] = book;
        last++;
    }

    public int getLength(){
        return last;
    }

    @Override
    public Iterator iterator() {
        return new BookShelfIterator(this);
    }
}

3.5 普通的 pojo Book

public class Book {

    private String name;

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

3.6 测试代码

public class IteratorTest {
    public static void main(String[] args) {
        BookShelf bookShelf = new BookShelf(4);
        bookShelf.appendBook(new Book("A"));
        bookShelf.appendBook(new Book("B"));
        bookShelf.appendBook(new Book("C"));
        bookShelf.appendBook(new Book("D"));
        // 核心业务代码
        Iterator iterator = bookShelf.iterator();
        while (iterator.hasNext()){
            Book book = (Book) iterator.next();
            System.out.println(book.getName());
        }
    }
}

uml.png

4. 我们再来看看这个类图关系并思考问题:

  1. 我们为什么要使用 Iterator?

首先我们要分析需求,我们希望以调用者的角度去遍历每个元素。
在业务代码中,如果我们直接拿到容器 Books[],然后用 for 循环获取元素。这种方法完全可以实现。
可是假设,有一天我们换了新的书架,改用 List<Book> Books。我们该怎么处理呢?我们只需要修改,BookShelf 和 BookShelfIterator 就达成目的了。而我们的核心业务代码部分,没有做任何的修改。
这说明 这里的 while 循环并不依赖于 BookShelf 的实现。

  1. 在我看这个设计模式的时候,我存在一个疑问,为什么还要准备一个 Aggregate 接口?

我们可以想这样一个问题,在 BookShelf 类是实现了 Aggregate 接口,然后重写 iterator() 的方式获取到的遍历器。
如果我们的 BookShelf 类 不实现 Aggregate 接口,并且方法直接写成

public getBookShelfIterator(){
      return new BookShelfIterator(this); 
}

这样大家直接可以看出这种写法太过于耦合了,因为我们在核心业务代码中,只能使用

Iterator iterator = bookShelf.getBookShelfIterator();

同样的道理,我们让 BookShelf 类实现 Aggregate 接口,表示了 BookShelf 类具备了一种能力。
一种我们可以拿到 iterator 并用 hasNext() 以及 next() 的方法进行遍历的能力。
此外,当我们 需要使用其他遍历器来完成操作时,只需要 写一个新的遍历器实现类,并在重写方法内部并创建并用于返回即可,核心代码的逻辑还是没有改变。



二、Adapter 模式

对于包装器的模式,在 Spring Framework 的底层有大量的使用,

A) 类适配器模式(使用继承手段)

1. 场景

目前我们的固定插座是没有办法改变的,而我买回来的电脑充电线也是没有办法改变的,总不能直接换个电脑吧,那我想一个折中的办法,中间加一个组件,使两者联系起来。


image.png

2. UML

image.png

3. Code

3.1 Print 接口也就是我们的需求,对应于图中固定的插座(他规定了使用的条件,100伏特)
/**
 * @author CSZ
 */
public interface Print {
    void printWeak();
    void printStrong();
}
3.2 Banner 类对应我们的电脑插头,也是固定的(12伏特,可以使用)
/**
 * @author CSZ
 */
public class Banner {
    private String string;
    public Banner(String string){
        this.string = string;
    }
    public void showWithParen(){
        System.out.println("( " + string +" )");
    }
    public void showWithAster(){
        System.out.println("* " + string + " *");
    }
}
3.3 PrintBanner 作为两者的调和者,继承了 Banner 的能力,并且针对 Print 接口要求,进行了适配
public class PrintBanner extends Banner implements Print{

    public PrintBanner(String string){
        super(string);
    }

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

    @Override
    public void printStrong() {
        showWithAster();
    }
}
3.4 测试代码
/**
 * @author CSZ
 */
public class MainTest {
    public static void main(String[] args) {
        // 对于 Print 是功能的声明者,他用于指明要使用的方法
        // 对于Banner 来说,他是功能的完成者
        // 但是由于 Banner 不符合 Print 的要求,PrintBanner 继承了Banner的功能,有为了符合 Print 要求而做了调整。
        // 所以其中的奥妙就在于继承 Banner 并调用了 Banner 中的方法,获得了Banner的能力
        Print banner = new PrintBanner("Hello");
        banner.printWeak();
        banner.printStrong();
    }
}

B) 对象适配器模式(使用委托)

1. 场景没有变化 唯一变化的是现在 Print 也成了类

java 中,不允许多继承,这样的情况又该怎样处理

2. UML

image.png

其实如果 我们熟悉 UML 可能已经看明白了,很简单吗,作为 PrintBanner 我不能多继承,这里就干脆只继承 Print 类,Banner 呢,从 UML 中我们可以看到,这是一种聚合关系,即使Banner 变成我的组成部分之一,也就是 has -a 的关系。

3. Code

其中,测试方法和 Banner 类没有代码调整

3.1 Print 变成了类
public abstract class Print {
    public abstract void printWeak();
    public abstract void printStrong();
}
3.2 PrintBanner 也做调整
/**
 * @author CSZ
 */
public class PrintBanner extends Print{

    private Banner banner;
    public PrintBanner(String string){
        this.banner = new Banner(string);
    }
    
    @Override
    public void printWeak() {
        banner.showWithParen();
    }

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

image.png

4. 再次回顾UML 总结与思考:

  1. 一个很本质的问题,无论是A方式还是B方式,前提是两者是具有一定的联系(类似的功能),比如一个是用100伏特的电,一个是用 0.07mpa 的水压。你再怎么适配也是没有价值,因为两个完全没有联系。
  2. 我们已经正式上线的代码,经过了时间的检测,是没有质量问题的,而此刻我们新的开发中如果需要其中的功能,我们应该使用适配器模式创建一个新的类,将原有的类继承或者实现,并在新的类中完成对新的接口的适配。这样我们原有的代码就可以保持不变,而且如果新的代码出现了问题,我们使用新的适配器类,也可以更容易定位问题。
  3. 最后让我们看一下 spring 源码叭:
    image.png

    AbstractApplicationContext类中的如下方法:用来创建后置处理器工厂
    image.png

根据继承关系我们找到了ResourceAdapterApplicationContext,他到底做了怎样的 wapper呢?
通过重写 postProcessBeanFactory 方法,来适配关于 Resource 的后置处理器工厂的加载创建。

image.png

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容

  • Iterator设计模式 简介: Iterator设计模式在数据集合中按照顺序遍历集合. 英语单词Iterate有...
    TheAPICaller阅读 205评论 0 0
  • 示例程序 实现了Iterator模式的示例程序的作用是将书放置到书架中,并将书的名字顺序显示出来。 示例程序的UM...
    憨憨二师兄阅读 188评论 0 0
  • 迭代器模式定义 迭代器模式(Iterator),提供一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内...
    嘟嘟碰碰叮叮当当阅读 19,361评论 2 9
  • 迭代器模式定义迭代器模式(Iterator),提供一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内部...
    丶_62f3阅读 125评论 2 2
  • 迭代器模式(Iterator)用于在数据集合中按照顺序遍历集合,在遍历的同时不需要暴露对象的内部表示,根据不同的需...
    Code4Android阅读 375评论 0 3