设计模式学习总结(一)

129 阅读23分钟
登录 注册写文章 首页下载APP

设计模式学习总结(一)

AxeChen关注赞赏支持

设计模式学习总结(一)

字数 4660阅读 0

学习设计模式本来是我在找工作时用来复习的Java基础。参考了《Android 源码设计模式解析与实战》一书和其他博主的博客,再加上自己的理解和总结,就有了这篇博客。

设计模式可以说是和每一个开发者都密切相关,比如像一个安卓开发程序员,使用RecyclerView的setAdapternotifidatasetchange这两个方法就用到了适配器模式和观察者模式,同时如果想成为一个架构师,这些设计模式的知识也会让你搭建框架的时候得心应手。

目前设计模式为23种,这些是目前公认的,我的理解是:如果自己有更好的想法也可以写出更好的设计模式,就像mvc,mvp这些,都是别人参悟出来的一些架构方式,后来被人公认才广泛流行起来,当然设计模式也不是都一定是完美的,每个设计模式都是优缺点并存。

本文将介绍部分设计模式:

  • 构造者模式
  • 工厂模式
  • 责任链模式
  • 观察者模式
  • 原型模式
  • 代理模式
  • 单例模式
  • 状态模式
  • 策略模式
  • 适配器模式

其他的设计模式等学习完之后,再去补充一篇博客(二)即可。

1、构造者模式

如果用过Retorfit、OKHttp等对以下代码一定不会陌生。

    public void init() {
        // 初始化okhttp
        OkHttpClient client = new OkHttpClient.Builder()
                .build();

        // 初始化Retrofit
        retrofit = new Retrofit.Builder()
                .client(client)
                .baseUrl(Request.HOST)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    }

Retrofit的初始化就是一个典型的构造者模式。

1.1、构造者模式的定义

将一个复杂对象的构建和它的初始化进行分离,使得同样的构建过程可以创建不同的表示。

1.2、用构造者模式实现ImageLoader。

首先先简单分析下自己如果要实现一个ImageLoader需要具备那部分。


ImageLoader的结构

示例只对ImageLoader需要的缓存机制和下载器做一些配置。
ImageLoader是一个高度拓展的一个图片框架,最为突出的一点就是,ImageLoader中的组件很大部分是可以自己定制的,只要符合ImageLoader定义的接口规范即可。
定义一个ImageLoaderConfig来作为配置ImageLoader参数的类。

package com.axe.builder.imageloader;

/**
 * 图片下载框架的配置
 * 
 * @author 11373
 *
 */
public class ImageLoaderConfig {

    /**
     * 使用内存缓存
     */
    public boolean useMemoryCache = false;

    /**
     * 使用自定义的内存缓存
     */
    public boolean useCustomMemoryCache = false;

    /**
     * 使用磁盘缓存
     */
    public boolean useDiskCache = false;

    /**
     * 使用自定义的磁盘缓存
     */
    public boolean useCustomDiskCache = false;

    /**
     * 是否使用自定义的下载器
     */
    public boolean useCustomDownLoader = false;

    /**
     * 内存缓存
     */
    public Cache memoryCache;

    /**
     * 磁盘缓存
     */
    public Cache diskCache;

    /**
     * 下载器
     */
    public DownLoader downLoader;

    /**
     * 构造者
     * 
     * @author 11373
     *
     */
    public static class Builder {

        /**
         * 内存缓存
         */
        public Cache memoryCache;

        /**
         * 磁盘缓存
         */
        public Cache diskCache;

        /**
         * 下载器
         */
        public DownLoader downLoader;

        public boolean useMemoryCache = false;

        public boolean useDiskCache = false;

        public Builder setDownLoader(DownLoader downLoader) {
            this.downLoader = downLoader;
            return this;
        }

        public Builder setMemoryCache(Cache memoryCache) {
            this.memoryCache = memoryCache;
            return this;
        }

        public Builder setDiskCache(Cache diskCache) {
            this.diskCache = diskCache;
            return this;
        }

        public Builder userMemoryCache(boolean flag) {
            this.useMemoryCache = flag;
            return this;
        }

        public Builder useDiskCache(boolean flag) {
            this.useDiskCache = flag;
            return this;
        }

        public void applyConfig(ImageLoaderConfig config) {

            if (useMemoryCache) {
                config.useMemoryCache = true;
                if (memoryCache != null) {
                    config.memoryCache = memoryCache;
                } else {
                    // 如果外部没有传入自己的内存缓存策略,则使用默认的
                    config.memoryCache = new MemoryCache();
                }
            }

            if (useDiskCache) {
                config.useDiskCache = true;
                if (diskCache != null) {
                    config.diskCache = diskCache;
                } else {
                    // 如果外部没有传入自己的内存缓存策略,则使用默认的
                    config.diskCache = new DiskCache();
                }
            }

            if (downLoader != null) {
                config.downLoader = downLoader;
                config.useCustomDownLoader = true;
            } else {
                // 如果外部没有传入自己的内存缓存,则使用默认的
                config.downLoader = new DeFaultDownLoader();
            }
        }

        public ImageLoaderConfig create() {
            ImageLoaderConfig config = new ImageLoaderConfig();
            applyConfig(config);
            return config;
        }
    }

}

而ImageLoader这个类只用于下载图片,它的核心方法是displayImage,然后他的所有配置都来自ImageLoaderConfig:

package com.axe.builder.imageloader;

/**
 * 图片下载器
 * 
 * @author 11373
 *
 */
public class ImageLoader {

    private ImageLoaderConfig config;

    private static ImageLoader imageLoader;

    private ImageLoader() {
    }

    public static ImageLoader getInstance() {
        if (imageLoader == null) {
            synchronized (ImageLoader.class) {
                if (imageLoader == null) {
                    imageLoader = new ImageLoader();
                }
            }
        }
        return imageLoader;
    }

    // 初始化
    public void init(ImageLoaderConfig config) {
        this.config = config;
    }

    public void displayImage(String url, ImageView image) {
        Bitmap bitmap = null;
        // 如果设置了内存缓存,就使用内存缓存。
        if (bitmap == null && config.useMemoryCache) {
            bitmap = config.memoryCache.getBitmap(url);
        }
        // 设置了磁盘缓存,就是使用磁盘缓存。
        if (bitmap == null && config.useDiskCache) {
            bitmap = config.diskCache.getBitmap(url);
        }
        // 如果内存和磁盘都找不到图片,那就去下载
        if (bitmap == null) {
            config.downLoader.downloadImage(url);
        }
        image.setBitmap(bitmap);
    }
}

初始化和使用:

public class BuilderMain {

    public static void main(String[] args) {
        ImageLoaderConfig config = new Builder()
                .setDiskCache(new AxeDiskCache())
                .setMemoryCache(new AxeMemoryCache())
                .useDiskCache(true)
                .userMemoryCache(true)
                .setDownLoader(new AxeImageLoader())
                .create();
        ImageLoader loader = ImageLoader.getInstance();
        loader.init(config);
        // 使用
        loader.displayImage("", null);
    }
}
1.3、构造者模式的优缺点

优点:良好的封装性,使用构造者模式可以使客户端不必知道产品内部的组成细节。构造者独立存在,便于拓展。 缺点:会产生多余的构造者对象,消耗内存。

2、工厂模式

2.1、工厂方法模式

这个简单的来说,就是面向对象的多态实现,建一个创建对象的接口,具体创建什么接口由子类自己决定。就相当于一个有一个创建汽车的工厂,具体有宝马汽车工厂和奔驰汽车工厂实现了它,具体是造出宝马车还是奔驰车由这两个方法决定。

  • 创建一个汽车的实现类,封装了汽车的公共方法,开车。
public interface Car {
    void drive();
}
public class BenziCar implements Car {
    @Override
    public void drive() {
        System.out.println("驾驶奔驰车");
    }
}
public class BWMCar implements Car{
    @Override
    public void drive() {
        System.out.println("宝马车在驾驶");
    }
}

  • 创建一个公共的方法,都有一个公共的属性就是创建汽车。然后奔驰工厂和宝马工厂都会继承它,并且都有一个公共的方法,那就是创建汽车,也就是都有一个创建对象的方法。
public interface Factory {
    Car createCar();
}
public class BenziFactory implements Factory{
    @Override
    public Car createCar() {
        return new BenziCar();
    }
}
public class BWMCarFactory implements Factory {
    @Override
    public Car createCar() {
        return new BWMCar();
    }
}

实际使用:

public class FactoryMain {
    public static void main(String[] args) {
        Factory benziFactory = new BenziFactory();
        benziFactory.createCar().drive();
        
        Factory bwmFactory = new BWMCarFactory();
        bwmFactory.createCar().drive();
    }
}
驾驶奔驰车
宝马车在驾驶

工厂方法模式的优缺点

  • 优点:符合开放封闭原则。新增产品时,只需增加相应的具体产品类和相应的工厂子类即可;符合单一职责原则。每个具体工厂类只负责创建对应的产品。(本段文字来自博客: Android的设计模式-工厂方法模式
  • 缺点:一个具体工厂只能创建一种具体产品;增加新产品时,还需增加相应的工厂类,系统类的个数将成对增加,增加了系统的复杂度和性能开销;引入的抽象类也会导致类结构的复杂化。(本段文字来自博客:Android的设计模式-工厂方法模式
2.2、简单工厂模式

继续2.1的例子,我们将所有的实现的汽车工厂变成一个工厂,这个工厂可以生成各种不同的汽车。修改汽车工厂的基类如下:

public interface Factory2 {
    Car createCar(String name);
}

我们只要传入我们想生成的汽车名字,我们即可生成改汽车的对象。 实现类如下:

public class CarFactory implements Factory2 {
    @Override
    public Car createCar(String name) {
        if ("benzi".equals(name)) {
            return new BenziCar();
        } else if ("bwm".equals(name)) {
            return new BWMCar();
        }
        return null;
    }
}

实际使用时:

    Factory2 factory2 = new CarFactory();
    factory2.createCar("benzi").drive();
    factory2.createCar("bwm").drive();

还有一种用方式的去实现这个公用的工厂:

public class CarFactory2 {
    public static <T extends Car> T createCar(Class<T> clz) {
        Car car = null;
        try {
            car = (Car) Class.forName(clz.getName()).newInstance();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } 
        return (T) car;
    }
}

实际使用时:

CarFactory2 factory3 = new CarFactory2();
factory3.createCar(BenziCar.class).drive();
factory3.createCar(BWMCar.class).drive();

简单工厂模式的优缺点

  • 优点:代码解耦,创建实例的工作与使用实例的工作分开,使用者不必关心类对象如何创建。
  • 缺点:从第一种实现方式就可以看到,会增加很多的if判断,当要产生的对象变多的时候不便于维护;违背开放封闭原则,若需添加新产品则必须修改工厂类逻辑,会造成工厂逻辑过于复杂;简单工厂模式使用了静态工厂方法,因此静态方法不能被继承和重写;工厂类包含了所有实例(产品)的创建逻辑,若工厂类出错,则会造成整个系统都会会受到影响。(本段文字来自博客: Android的设计模式-工厂方法模式
2.3、抽象工厂模式

现在造一辆车的一些零件肯定是由各种不同的厂商生产的。宝马汽车和奔驰汽车的引擎和轮子也不是同一个厂商生产的,那么如果将所有的这些零件工厂也抽象出来,代码表示如下: 零件的接口:

public interface Engine {
    void getEngine();
}

public interface Wheel {
    void getWheel();
}

零件的实现类:

public class AKMWheel implements Wheel{
    @Override
    public void getWheel() {
        System.out.println("AKM轮子");
    }
}

public class M416Wheel implements Wheel{
    @Override
    public void getWheel() {
        System.out.println("M426轮子");
    }
}

public class M24Engine implements Engine {
    @Override
    public void getEngine() {
        System.out.println("M24引擎");
    }
}
public class AWMEngine implements Engine {
    @Override
    public void getEngine() {
        System.out.println("AWM引擎");
    }
}

创建汽车的接口:

public interface Factory3 {
    Wheel createWheel();
    Engine createEngine();
}

宝马汽车和奔驰汽车的具体实现:

public class BenziCarFactory implements Factory3 {
    @Override
    public Wheel createWheel() {
        return new AKMWheel();
    }
    @Override
    public Engine createEngine() {
        return new M24Engine();
    }
}
public class BWMCarsFactory implements Factory3{
    @Override
    public Wheel createWheel() {
        return new M416Wheel();
    }

    @Override
    public Engine createEngine() {
        return new AWMEngine();
    }
}

具体使用:

    System.out.println("创建宝马车");
    Factory3 bwFactory = new BWMCarsFactory();
    bwFactory.createEngine().getEngine();
    bwFactory.createWheel().getWheel();
    System.out.println("创建奔驰车");
    Factory3 bzFactory = new BenziCarFactory();
    bzFactory.createEngine().getEngine();
    bzFactory.createWheel().getWheel();
创建宝马车
AWM引擎
M426轮子
创建奔驰车
M24引擎
AKM轮子

抽象工厂模式的优缺点

  • 优点:代码解耦,创建实例的工作与使用实例的工作分开,使用者不必关心类对象如何创建。
  • 缺点: 如果增加新的产品,则修改抽象工厂和所有的具体工厂,违反了开放封闭原则(本段文字来自博客:Android的设计模式-工厂方法模式

3、策略模式

继续用英雄联盟来举例。

在这个游戏中获取赏金的方式有很多种,比如:击杀野怪、击杀英雄、击杀小兵等等。如果让我写一个计算赏金的方法,我会怎么写呢。

package com.axe.strategy;

/**
 * 赏金类
 * 
 * @author 11373
 *
 */
public class Bounty {

    /**
     * 小兵
     */
    public static final int TYPE_BATMAM = 0;

    /**
     * 英雄
     */
    public static final int TYPE_HERO = 1;

    /**
     * 野怪
     */
    public static final int TYPE_MONSTER = 2;

    /**
     * 大龙
     */
    public static final int TYPE_DRAGON = 3;

    /**
     * 获取赏金的方法
     * 
     * @param type  类型
     * @param count 数量
     * @return
     */
    public int killGetMoney(int type, int count) {
        int sum = 0;
        if (type == TYPE_BATMAM) {
            sum += getBatmanMoney(count);
        } else if (type == TYPE_HERO) {
            sum += getHeroMoney(count);
        } else if (type == TYPE_MONSTER) {
            sum += getMonsterMoney(count);
        } else if (type == TYPE_DRAGON) {
            sum += getDragonMoney(count);
        }
        return sum;
    }

    /**
     * 获取小兵的钱
     * 
     * @param count
     * @return
     */
    private int getBatmanMoney(int count) {
        return count * 30;
    }

    /**
     * 获取英雄的钱
     * 
     * @param count
     * @return
     */
    private int getHeroMoney(int count) {
        return count * 300;
    }

    /**
     * 获取野怪的钱
     * 
     * @param count
     * @return
     */
    private int getMonsterMoney(int count) {
        return count * 50;
    }
    
    /**
     * 获取大龙的钱
     * @param count
     * @return
     */
    private int getDragonMoney(int count) {
        return count * 500;
    }

}

在实际使用的时候:

Bounty bounty = new Bounty();
int getMoney = bounty.killGetMoney(Bounty.TYPE_BATMAM, 10) + bounty.killGetMoney(Bounty.TYPE_HERO, 1)
            + bounty.killGetMoney(Bounty.TYPE_MONSTER, 6);

看起来问题不大,也比较简洁。但是如果这个是一个真实的英雄联盟中的例子,那么情况会有这么简单吗?比如:英雄联盟中的击杀野怪,野怪有很多种,获得的赏金也不一样;击杀英雄的时候,不一定就是300个金币。还有某些辅助装备会导致金币不一样。而且这里覆盖的击杀种类肯定不止这么多。比如推掉防御塔也有金币,推掉水晶也有金币等等。如果按照所有的情况去计算的话,Bounty 这个类将会非常庞大,里面的逻辑判断也会非常多。

    /**
     * 获取赏金的方法
     * 
     * @param type  类型
     * @param count 数量
     * @return
     */
    public int killGetMoney(int type, int count) {
        int sum = 0;
        if (type == TYPE_BATMAM) {
            sum += getBatmanMoney(count);
        } else if (type == TYPE_HERO) {
            sum += getHeroMoney(count);
        } else if (type == TYPE_MONSTER) {
            sum += getMonsterMoney(count);
        } else if (type == TYPE_DRAGON) {
            sum += getDragonMoney(count);
        }
        return sum;
    }

这个方法的if嵌套层数会非常多。if条件太多维护起来绝对是痛苦,并且会带来更多的错误!如果使用策略模式去优化的话,如何去写呢?

  • 定义要给获取赏金的规则
/**
 * 获取赏金的规则
 * @author 11373
 *
 */
public interface GetMoney {
    public int getMoney(int count);
}

  • 将每种类型的获取赏金的方法继承该接口
/**
 * 小兵赏金计算器
 * @author 11373
 *
 */
public class BatmanCalculater implements GetMoney {

    @Override
    public int getMoney(int count) {
        return count * 30;
    }

}
  • 定义一个类去控制赏金获取的方式
/**
 * 赏金计算器
 * 
 * @author 11373
 *
 */
public class BountyCalculater {
    private GetMoney getMoney;

    public int getBountyMoney(int count) {
        return getMoney.getMoney(count);
    }

    public void setGetMoney(GetMoney getMoney) {
        this.getMoney = getMoney;
    }
}

实际的使用情况。遇到不同的获取赏金的情况时,只需要替换不同的计算规则即可。

// 使用策略模式
        int sum = 0;
        BountyCalculater calculater = new BountyCalculater();

        calculater.setGetMoney(new BatmanCalculater());
        sum += calculater.getBountyMoney(10);

        calculater.setGetMoney(new HeroMoneyCalculater());
        sum += calculater.getBountyMoney(5);

        calculater.setGetMoney(new DragonCalculater());
        sum += calculater.getBountyMoney(1);

        calculater.setGetMoney(new MonsterCalculater());
        sum += calculater.getBountyMoney(34);
        System.out.println(sum);

现在看来应该是增加了不少的类,这个就是策略模式的一个劣势。 但是如果从类的单一性原则和产品可维护阶段来说,就会感觉这个策略模式的妙用之处。

  • 类的单一性原则
    每个种类的计算规则都单独封装成一套计算方法,修改了某一套计算方法不会对其他的计算规则产生影响。
  • 可维护和拓展
    假如要新增一个计算规则,只需要继承GetMoney即可,不会对其他的计算规则产生任何的影响。
3.1、策略模式的优缺点

优点:结构清晰明了,使用简单直观;耦合度比较低,便于拓展; 缺点:随着策略的增多,策略的子类也会增多。

4、单例模式

这种是比较常见的模式了,如果是封装什么网络请求框架、图片请求框架,在整个app只需要一个全局对象的时候都会用到这个模式。很多博客应该已经把这个东西介绍得很清楚,单例模式的意义就是——让整个程序中只有唯一的一个对象。

4.1、饿汉模式
/**
 * Created by Axe on 2017/8/29.
 * <p>
 * 单例模式 - 饿汉模式
 *  最简单的单例模式,具有单利模式的所有特征
 *  缺点:
 *  1、当类加载时就会初始化成员变量,可能会浪费资源。
 *  2、在多线程情况下,不安全,无法保证对象唯一。
 */

public class HungryModeSingleton {

    private static final HungryModeSingleton singleton = new HungryModeSingleton();

    private HungryModeSingleton() {
    }

    public static HungryModeSingleton getInstance() {
        return singleton;
    }
}
4.2、懒汉模式
/**
 * Created by Axe on 2017/8/29. 
 * 1、懒汉模式只有调用的时候才初始化,节省了开支。
 * 2、懒汉模式保证了线程安全
 * 缺点:
 * 每次初始化会进行同步,会消耗不必要的资源
 */

public class LazyModeSingleton {

    private static LazyModeSingleton singleton;

    private LazyModeSingleton() {
    }

    public static synchronized LazyModeSingleton getInstance() {
        if (singleton == null) {
            singleton = new LazyModeSingleton();
        }
        return singleton;
    }

}

4.3、双层检测通道模式(线程安全)
/**
 * Created by Axe on 2017/8/29.
 * <p>
 * Double Check Lock (双层检查同步模式)
 */

public class DCLSingleton {
    private static DCLSingleton singleton = null;

    private DCLSingleton() {
    }

    public static DCLSingleton getInstance() {
        if (singleton == null) {
            synchronized (DCLSingleton.class) {
                if (singleton == null) {
                    singleton = new DCLSingleton();
                }
            }
        }
        return singleton;
    }
}
4.4、内部类模式
/**
 * Created by Axe on 2017/8/29.
 * <p>
 * 静态内部类单例模式
 */

public class StaticInnerClassSingleton {

    private StaticInnerClassSingleton() {
    }

    public static StaticInnerClassSingleton getInstance() {
        return SingletonHolder.singleton;
    }

    private static class SingletonHolder {
        private static final StaticInnerClassSingleton singleton = new StaticInnerClassSingleton();
    }
}

##### 4.5单例模式的优缺点
优点:整个内存中只有一个对象,减少了内存消耗,减少了系统性能的开销。
缺点:持有Context时,可能导致内存泄漏;扩展比较困难,只能修改源代码来修改拓展。

5、状态模式

一看到状态模式,就能想到,这个肯定是和”状态“有关。 继续用打英雄联盟举例(今天和它杠上了):

现在生活中有一个这样的情况,如果要打英雄联盟,那电脑就必须开机,此时的电脑的状态为NO,如果电脑没开机就无法玩英雄联盟,此时的电脑状态为OFF。如果现在去写一个玩英雄联盟但是依赖电脑状态的类,如果电脑开机就可以玩游戏,电脑没有开机就不玩游戏,并提示开机才能玩游戏,那该如何写呢? 在没有学习状态模式的时候,我是这么写的:

public class PlayGameController implements IComputerActivity {

    /**
     * 表示关机状态
     */
    private static final int OFF = 0;

    /**
     * 表示开机状态
     */
    private static final int NO = 1;

    private int state = 0;

    public void setSate(int state) {
        this.state = state;
    }

    @Override
    public void playGame() {
        if (state == OFF) {
            System.out.println("请先开机再打游戏");
        } else {
            System.out.println("正在打游戏");
        }

    }

    @Override
    public void watchMovie() {
        if (state == OFF) {
            System.out.println("请先开机再看电影");
        } else {
            System.out.println("正在看电影");
        }
    }
}

这里定义了电脑的一些行为接口IComputerActivity ,这些电脑暂时有两个方法:playGame和watchMovie。 打游戏和看电影都必须在开机之后才能执行的操作,所以这里在这两个方法中都加了判断电脑是不是开机的状态:

        if (state == OFF) {
            System.out.println("请先开机再打游戏");
        } else {
            System.out.println("正在打游戏");
        }

在状态比较少和电脑的行为比较少时问题不大。但是假如电脑的状态变多,这个if条件将会变得很繁琐,比如if判断会变成这样:

        if (state == OFF) {
            System.out.println("请先开机再打游戏");
        } else  if(state == xxx){
            System.out.println("正在打游戏");
        }else if(state == rrr){
         }
        ... ... 此处省略若干if条件

假如电脑的行为不仅仅是玩游戏和看电影,还有数个行为的话,那这些if判断每个行为中都要写一遍。重复的if判断维护起来也非常麻烦,也更加容易出错。那有没有办法让这些行为能单独处理,一个类只处理一个状态?

那么用状态模式来重构这些代码。 1、电脑开机状态的处理:

public class PowerNoState implements IComputerActivity{

    @Override
    public void playGame() {
        System.out.println("正在打英雄联盟");
    }

    @Override
    public void watchMovie() {
        System.out.println("正在看火影忍者");
    }
}

2、电脑关机状态的行为处理:

/**
 * 状态模式 :当电脑电源关闭之后的操作
 * @author 11373
 *
 */
public class PowerOffState implements IComputerActivity{

    @Override
    public void playGame() {
        System.out.println("请开机玩游戏");
    }

    @Override
    public void watchMovie() {
        System.out.println("请开机看电影");
    }

}

3、然后定义好电脑电源

public interface PowerController {
    public void powerOn();
    public void powerOff();
}

当调用powerOn时我们就初始化PowerNoState,当调用powerOff就初始化PowerOffState这样

5.1、状态模式的优缺点

优点:将每一个状态单独封装成子类,便于维护和拓展;能减少过多的条件语句,使结构更加清晰,提高代码的维护性。

缺点:当状态很多时,必然会增加状态子类的个数。

6、观察者模式

如果用过RxJava就会接触到观察者模式了。定义对象的一种一多的依赖关系,则所有的依赖于它的对象都会得到通知并且自动更新。
举一个游戏中的简单的例子,比如在英雄联盟中,易大师穿着复活甲被杀死了,这个时候盖伦和艾希都在等待易大师复活,再对他进行攻击。
那么,这里的观察者就是盖伦和艾希。他们有一个共同的行为就是打击的操作。

/**
 * 观察者
 * @author 11373
 *
 */
public interface Observer {
    /**
     * 每一个观察者都有一个攻击的方法
     * @param name
     */
    public void hit(String name);
}

那易大师能被其他人观察到它的状态,并当他发生状态改变的时候进行改变。

/**
 * 被观察类
 * 
 * @author 11373
 *
 */
public interface Obserable {

        //提供的能被观察者观察到的方法
    public void addObserver(Observer observer);
    public void removeObserver(Observer observer);

      // 英雄复活的方法
    public void resurgence(String name);
}

盖伦的实现:

/**
 * 观察者实现类
 * @author 11373
 *
 */
public class GaLenObserverImpl implements Observer{

    @Override
    public void hit(String name) {
        System.out.println("我是盖伦,"+name+"复活了,快打他");
    }

}

易大师的实现:

/**
 * 易大师,被观察者
 * 
 * @author 11373
 *
 */
public class YiObserableImpl implements Obserable {

    private List<Observer> observers = new ArrayList<>();

    @Override
    public void addObserver(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        if (observers.contains(observer)) {
            observers.remove(observer);
        }
    }

    @Override
    public void resurgence(String name) {
        for (Observer observer : observers) {
            observer.hit(name);
        }
    }
}

最后的执行效果:

    public static void main(String[] args) {
        Observer aich = new AichObserverlmpl();
        Observer galen = new GaLenObserverImpl();
        Obserable yi = new YiObserableImpl();
        yi.addObserver(aich);
        yi.addObserver(galen);
// 易大师复活的行为
        yi.resurgence("易大师");
    }
我是艾希易大师复活了,快打他
我是盖伦易大师复活了,快打他

6.1、观察者模式的优缺点

优点:观察者对象和被观察者对象解耦,双方依赖都依赖抽象,而不是依赖具体对象。

缺点:依赖关系并未完全解除,抽象主题任然依赖抽象观察者;使用观察者模式时需要考虑一下开发效率和运行效率的问题,程序中包括一个被观察者、多个观察者,开发、调试等内容会比较复杂,而且在Java中消息的通知一般是顺序执行,那么一个观察者卡顿,会影响整体的执行效率,在这种情况下,一般会采用异步实现。

7、代理模式

在玩游戏的时候,通常有这种情况发生。有些人玩游戏技术并不咋地,但是段位却老高。以上就是常见的代理模式啦!

把以上的情况变成代码该如何表示呢? 定义玩家接口:

 * 游戏玩家接口
 * 
 * @author 11373
 *
 */
public interface IPlay {

    /**
     * 登录游戏
     */
    void loginGame();

    /**
     * 打游戏
     */
    void play();

    /**
     * 赢得比赛
     */
    void winGame();

}

定义普通玩家接口:

/**
 * 游戏玩家类,实际要赢得游戏的玩家
 * @author 11373
 *
 */
public class GamePlayer implements IPlay{

    @Override
    public void loginGame() {
        System.out.println("游戏玩家登录游戏");
    }

    @Override
    public void play() {
        System.out.println("游戏玩家开始打游戏");
    }

    @Override
    public void winGame() {
        System.out.println("游戏玩家赢得了比赛");
    }

}

定义代打接口:

/**
 * 靠代打游戏生存的游戏代打
 * 
 * @author 11373
 *
 */
public class PlayerProxy implements IPlay {

    private IPlay player;

    public void setIPlyer(IPlay player) {
        this.player = player;
    }

    @Override
    public void loginGame() {
        player.loginGame();
    }

    @Override
    public void play() {
        player.play();
    }

    @Override
    public void winGame() {
        player.winGame();
    }

}

实际操作:

    public static void main(String[] args) {
        
        // 实际要打游戏的游戏玩家
        GamePlayer axeChen = new GamePlayer();
        
        // 游戏代打
        PlayerProxy gameProxy = new PlayerProxy();
        
        // 游戏代打知道要为谁代打游戏
        gameProxy.setIPlyer(axeChen);
        
        // 代打开始登录游戏
        gameProxy.loginGame();
        // 代打开始打游戏
        gameProxy.play();
        // 代打赢得了比赛
        gameProxy.winGame();
    }
7.1、代理模式的优缺点

推荐看下这篇博客的总结,www.jianshu.com/p/a0e687e09…

8、适配器模式

适配器模式在安卓开发经常可以见到,RecyclerView的setAdapter就是典型的适配器模式,将数据源传入,然后再适配不同的UI布局。
一个简单的例子去说明适配器模式:
比如生活中的手机电源,我们的电源通常是220v,但是手机上能接受的电源只有5V。这个是我们通常有一个手机的充电适配器去将220v的电压转为5v。接下来把这种情况变成代码。

定义适配器接口:

public interface Adapter {
    public int getVolt5();
}

电源实体类(实际输出电源):

public class Power {
    public int get220v() {
        return 220;
    }
}

电源适配器类,关键的适配操作:

public class PhoneAdapter implements Adapter {

    private Power power;

    public PhoneAdapter(Power power) {
        this.power = power;
    }

    @Override
    public int getVolt5() {
        return 5;
    }
}

代码测试:

      Power power = new Power();
        System.out.println("电源电压:"+power.get220v());
        PhoneAdapter adapter = new PhoneAdapter(power);
        System.out.println("通过是配置适配之后的电压:"+adapter.getVolt5());

输出结果:

电源电压:220
通过是配置适配之后的电压:5

以上的代码就是将输入的电压220v通过适配器转化成5v。RecyclerView的Adapter就是将数据源传入适配器(adapter)中,然后去适配不同的布局。 当然这边还有一种类适配器模式这边这边简单提下:

public class PhoneAdapter2 extends Power implements Adapter {

    @Override
    public int getVolt5() {
        System.out.println("电源电压:"+get220v());
        System.out.println("经过适配器适配后的电压:"+5);
        return 5;
    }
}

这边是用适配器继承数据源,同时实现适配器的接口。它的优势是无需持有数据源对象,只需继承数据源对象。

8.1、适配器模式的优缺点

优点:提高了类的复用性,适配器能让一个类有更广泛的用途;提高了灵活性,更换适配器就能达到不同的效果。不用时也可以随时删掉适配器,对原系统没影响。

缺点:过多的使用适配器,会让系统非常零乱,不易整体进行把握。明明调用A接口,却被适配成B接口。

9、责任链模式

以生活中的一个例子来解释责任链模式。比如公司的一个员工购买了一个办公用品,一共花费5000元,这个时候该员工去找部门经理审批报销,部门经理一看5000元已经大于他能审批的金额,于是就交给总监去审批。总监一看5000元也大于他能报销的金额,于是就交给老板去审批,老板能报销员工10w以内的金额,于是老板审批通过,同意了报销。

以上就是一个简单的责任链模式的例子,他的定义为:****

9.1、用代码来解释报销的案例

所有领导的相同点抽象,他们都能报销,有报销金额的范围等等。

public abstract class Leader {

    /**
     * 下一个执行者
     */
    public Leader nextHanlder;

    /**
     * 自身能够处理的最少金额
     * 
     * @return
     */
    public abstract int limit();

    /**
     * 报销金额的方法
     * 
     * @param money
     */
    public abstract void handle(int money);

    /**
     * 控制责任链的条件
     * 
     * @param money
     */
    public final void handleRequest(int money) {
        if (money <= limit()) {
            handle(money);
        } else {
            nextHanlder.handleRequest(money);
        }
    }
}

然后部门经理,总监,CEO都实现了这些方法。

/**
 * 经理级别最多报销1000
 * 
 * @author 11373
 *
 */
public class Manager extends Leader {

    @Override
    public int limit() {
        return 1000;
    }
    @Override
    public void handle(int money) {
        System.out.println("经理正在处理报销金额:"+money);
    }
}

public class CTO extends Leader{

    @Override
    public int limit() {
        return 5000;
    }

    @Override
    public void handle(int money) {
        System.out.println("CTO正在处理报销金额:"+money);
    }

}


public class CEO extends Leader{

    @Override
    public int limit() {
        return 100000;
    }

    @Override
    public void handle(int money) {
        System.out.println("CEO正在处理报销金额:"+money);
    }

}

最后测试:

    public static void main(String[] args) {
        CEO ceo = new CEO();
        CTO cto = new CTO();
        Manager manager = new Manager();
        manager.nextHanlder = cto;
        cto.nextHanlder = ceo;
        manager.handleRequest(5000);
    }

这里的执行结果是,经理无法报销,CTO能报销 于是CTO就报销了这笔金额。

CTO正在处理报销金额:5000
9.2、责任链模式的优缺点

优点:请求者和处理者关系解耦,处理者比较好的扩展。 缺点:处理者太多会影响性能,特别是循环递归的时候。

10、原型模式

原型模式的核心为clone方法,涉及java中的深拷贝和浅拷贝的知识。
这边关于深拷贝和浅拷贝的东西,涉及的东西比较多,这里就直接引用别人的博客吧.
www.jianshu.com/p/6d1333917…

参考书籍:《Android源码设计模式,解析与实战》
参考博客:www.jianshu.com/p/bf92927c9…
感谢博主四月葡萄的博客,他写的博客总结得比较好,建议去看看!我很多地方也是引用他写的链接。
代码地址: github.com/AxeChen/Des…

评论0 赞1赞 赞赏