前言
上文我们已经学习了关于Retrofit框架用到的建造者模式,单例模式以及工厂方法模式,接下来将继续复习剩余的外观模式,代理模式,策略模式,欢迎各位和我一起学习,查漏补缺
外观模式
外观模式也叫门面模式,属于结构型模式,简单来说,就是外部与内部必须通过统一的对象来进行(门面对象),提供接口,使得子系统更易于使用
实现方式
我们在使用有些付费软件的时候都需要去登录或者充值的吧,这些都是第三方SDK来完成的,我们只用进行简单的接入就可以使用相应的功能了
创建门面对象
可以封装SDK中的登录和会员支付接口,供外部子APP去调用
class VideoSDK {
fun login() {
val loginManager = LoginManager()
loginManager.login()
}
fun pay(money:Int) {
val payManager = PayManager()
payManager.pay(money)
}
}
//登录系统
class LoginManager {
fun login() {
println("打开登录界面")
println("进行登录操作")
println("登录成功")
}
}
//支付系统
class PayManager {
fun pay(momey: Int) {
println("生成订单信息")
println("选择支付方式")
println("支付成功:" + momey + "元")
}
}
Android中的应用
在Android外观模式也应用广泛,例如Conext类,它封装了很多重要的操作,比如startActivity()、sendBroadcast()等,这就是开发者调用的统一入口。Context是一个抽象类,它只是定义了抽象接口,真正的实现在ContextImpl类中
这样以来我们就可以简单调用个方法就可以启动一个Activity,Context已经将其具体的细节都封装好了
总结
优点
- 降低客户端与子系统之间的耦合度
- 通过外观类对子系统的接口封装,更易于使用
- 灵活性提高了,不管子系统如何变化,只要不印象我们的外观对象,可以自由修改,更好的划分层次
缺点
- 增加新的子系统的话,外观对象需要改变里面的代码的,违背了开闭原则
- 所有子系统的功能都要通过一个接口来实现的话,功能会会非常复杂
应用场景
- 对于一些层次划分明显的,例如医院的模式,这就是为什么医院门口会有相应的接待员进行服务
- 可以将一个子系统和客户端分离开来,这样子系统也可以移植到别的应用上去,具有一定的独立性
代理模式
无独有偶,这种模式如字面意思,简单来说就是为其他对象提供代理来控制对这个对象的访问
实现方式
一般我们使用代理模式有以下步骤
- 设置抽象主题类,这个是用来声明该对象和代理的公共方法
- 创建一个被代理类(被委托类),负责具体业务逻辑的执行,可以通过它来调用主体的方法
- 创建代理类,这个就是对被代理类的引用,在其中使用的接口方法去执行被代理类中对应的接口方法
- 最终的客户端类,这里就是使用代理模式的地方
可以说代理模式是属于结构型模式,下面我以代购为例子,来看看它的具体实现方式
首先创建抽象主题类
可以明确来说,代购一般都是有个购买方法,下面我创建People都有一个购买的方法
interface People {
//购买
fun buy()
}
创建具体的主题类
在国内的人定义个具体的购买过程,有个buy()方法,继承People接口,重写购买方法
class BuyerRelease(people: People) : People {
override fun buy() {
println("境内需要买一个海外护肤品")
}
}
使用代理类
这时候远在境外的代购人士需要知道你是谁,那个人想要买什么产品
class Oversea(people: People) :People {
private var mPeople:People?= people
override fun buy() {
println("海外代购人员上线.....")
mPeople?.buy() //直接调用被代理者方法
}
}
上述实现其实是代理模式的中的静态代理,那顾名思义,还有一种是动态代理,简单来说就是需要在运行的时候通过反射去生成,那就没有了代理类的字节码文件,它们之间的关系是由运行的时候才确定的
代理接口InvocationHandler,实现该接口需要重写invoke()方法
class DynamicProxy( //实现InvocationHandler接口
//被代理的对象
private val obj: Any
) : InvocationHandler {
//重写invoke()方法
@Throws(Throwable::class)
override fun invoke(proxy: Any?, method: Method, args: Array<Any?>?): Any {
println("海外动态代理调用方法: " + method.name)
return method.invoke(obj, args) //调用被代理的对象的方法
}
}
这样的话我们没必要像静态代理一样,接口新增一个方法就需要所有代理类实现这个方法,大大增加了代码的复杂程度,而动态代理就不一样了,不需要每一个方法都进行中转,可以灵活处理,复用性大大增强了
Android中的应用
最典型的例子就是retrofit网络请求库,这里简要提及下,为后文做铺垫
- 编写api接口
interface xxxApi {
@POST("app/xxxx")
@FormUrlEncoded
fun sendEmailCode(@Field("email") email: String?): Observable<BaseResponse<String>>
}
- 初始化Retrofit
var retrofit: Retrofit = Builder()
.baseUrl("https://xxxxxx")
.build()
- 动态创建当前实例,注意这里的create的方法就是使用到了动态代理模式
val service:xxxApi = retrofit.create(xxxApi::class.java)
service.sendEmailCode(xxxx)
总结
下面我们来总结下代理模式有什么优点和缺点
优点
- 降低耦合,让模块和系统之间
- 可以控制调用者的访问权限,起到一种保护的作用
缺点
- 由于我们增加了代理对象,所以可能会造成请求的处理速度变慢
- 有些代理模式实现起来非常复杂,会增加系统实现的复杂度
适用场景
- 一个对象不能或者不想直接访问另一个对象时,可以通过一个代理对象来间接访问
- 被访问的对象不想暴露全部内容时,可以通过代理去掉不想被访问的内容
策略模式
策略模式属于行为型模式,提供了一系列的算法,形成一个算法组给客户端调用,这样客户端可以根据不同的策略模式来解决不同的问题
实现方式
看到这里,对于策略模式还是一知半解,我们来举个例子来实现下,以休闲时间选择选择娱乐方式为例
定义个公共接口
这里就是我们如何选择的休闲娱乐方式
interface ChoiceStragety {
//娱乐方式
fun chase() //娱乐方法
}
创建具体策略类
实现抽象策略类的接口,具体的娱乐方式
class ShoppingStrategy : ChoiceStragety {
override fun chase() {
println("逛街咯~")
}
}
class MoviesStrategy : ChoiceStragety {
override fun chase() {
println("看电影咯~")
}
}
class GameStrategy : ChoiceStragety {
override fun chase() {
println("玩游戏咯~")
}
}
创建环境类
这里是需要操作不同的娱乐方式策略,可以使用不同的方式
class Context(//定义抽象策略类
private val chaseStragety: ChoiceStragety
) {
fun chase() { //执行具体策略对象的策略
chaseStragety.chase()
}
init { //构造方法传递具体策略对象过来
}
}
Android中的应用
- 我们使用到的ListView都需要设置Adapter,根据实际的需求使用不同的Adapter,这里就运用到了策略模式
listView = (ListView)findViewById(R.id.list_view);
//使用ArrayAdapter
listView.setAdapter(new ArrayAdapter<String>(this,R.id.item,new String[] {"one","two"}));
//使用BaseAdapter
listView.setAdapter(new BaseAdapter() {
@Override
public int getCount() {
return 0;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return null;
}
});
这里看下ListView的源码
public class ListView extends AbsListView {//相当于环境类
@Override
public void setAdapter(ListAdapter adapter) {//设置策略,即adapter
//其他代码略
}
}
public interface ListAdapter extends Adapter {//抽象策略接口
}
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {//具体策略类BaseAdapter,实现ListAdapter接口
}
public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSpinnerAdapter {//具体策略类ArrayAdapter,继承BaseAdapter,即实现ListAdapter接口
}
- 设置不同的Adapter(即不同的策略),我们就可以写出符合我们需求的ListView布局
- 我们常用的属性动画就是里面setInterpolator也是有用到策略模式的,感兴趣的小伙伴可以去详细了解下
总结
在Android开发中,策略模式也是用的非常多的,就比如说,让自己重新实现一个ViewPager,并且带有Indicator,会不会用到策略模式呢?
优点
- 策略模式等级结构定义了一个算,把公共代码移到父类当中,从而避免重复的代码
- 提供了可以替换继承关系的方法
- 避免使用多重条件判断语句
缺点
- 客户端调用的时候必须要知道所有的策略类,所以简单来说,策略模式只适用于那些客户端知道所有的算法或者行为的情况
- 通过依赖于环境的状态保存到客户端里面,将策略类设计成可共享的
应用场景
- 试想一下,如果一个应用里有许多类似的类,区别仅在于它们的行为,就可以使用策略模式动态选择其中一种行为
- 一个对象中有很多行为,如果不使用策略模式的话,就只好使用多重条件判断的语句