学习设计模式本来是我在找工作时用来复习的Java基础。参考了《Android 源码设计模式解析与实战》一书和其他博主的博客,再加上自己的理解和总结,就有了这篇博客。
设计模式可以说是和每一个开发者都密切相关,比如像一个安卓开发程序员,使用RecyclerView的setAdapter
和notifidatasetchange
这两个方法就用到了适配器模式和观察者模式,同时如果想成为一个架构师,这些设计模式的知识也会让你搭建框架的时候得心应手。
目前设计模式为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定义的接口规范即可。
定义一个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 createCar(Class 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.
*
* 单例模式 - 饿汉模式
* 最简单的单例模式,具有单利模式的所有特征
* 缺点:
* 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.
*
* 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.
*
* 静态内部类单例模式
*/
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 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、代理模式的优缺点
推荐看下这篇博客的总结,https://www.jianshu.com/p/a0e687e0904f
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中的深拷贝和浅拷贝的知识。
这边关于深拷贝和浅拷贝的东西,涉及的东西比较多,这里就直接引用别人的博客吧.
https://www.jianshu.com/p/6d1333917ae5
参考书籍:《Android源码设计模式,解析与实战》
参考博客:https://www.jianshu.com/p/bf92927c9d22
感谢博主四月葡萄的博客,他写的博客总结得比较好,建议去看看!我很多地方也是引用他写的链接。
代码地址:https://github.com/AxeChen/DesignMode