一周两个设计模式—设计模式之适配器模式(第二周)

举报
浮生闲半日 发表于 2021/11/12 10:34:20 2021/11/12
【摘要】 最近时间比较充裕,那就多写一些,顺便提升一下自己的组织能力和书面表达能力,毕竟好的文案也是十分必要的。今天介绍的是结构型模式的适配器模式,适配器模式的对象模式是后续的结构型模型的基础。定义: 将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。 适配器模式主要分为三种:类的适配器模式、对象的适配器模式和接口的适配器模式从定义中可以...

最近时间比较充裕,那就多写一些,顺便提升一下自己的组织能力和书面表达能力,毕竟好的文案也是十分必要的。今天介绍的是结构型模式的适配器模式,适配器模式的对象模式是后续的结构型模型的基础。

定义:

    将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

    适配器模式主要分为三种:类的适配器模式、对象的适配器模式和接口的适配器模式

从定义中可以很清楚的知道适配器模式的功能就是转换。

1272523-20190129142942342-2089942613.png

例如不同国家的插座标准不同,如果想使用中国的电器就需要适配器来进行中转。

适配器模式涉及3个角色:
    
    源(Adaptee):需要被适配的对象或类型,相当于插头。
    适配器(Adapter):连接目标和源的中间对象,相当于插头转换器。
    目标(Target):期待得到的目标,相当于插座。

类适配

类适配器模式.png

继承的关系,Adapter与Adaptee是继承关系,这决定了这是一个类适配器模式。
//源
open class Adaptee {
    fun method1(){
        Log.v("=======","方法1")
    }
}
//目标
interface Target {
    fun method1()
    fun method2()
}
//适配器
class Adapter:Adaptee(),Target{
    override fun method2() {
        Log.v("=======","方法2")
    }
}

这样就可以直接调用Adaptee中的method1()了。

var adapter = Adapter()
adapter.method1()
adapter.method2()

##对象适配器

与类适配器模式一样,为使客户端能够使用Adaptee类,我们把Adaptee与Target衔接起来。
但这里我们不继承Adaptee,而是把Adaptee封装进Adapter里。这里Adaptee与Adapter是组合关系
//适配器
class ObjectAdapter(adaptee: Adaptee):Target {

    private var adaptee = adaptee

    override fun method1() {
        adaptee.method1()
    }

    override fun method2() {
        Log.v("========","对象模式方法2")
    }
}

使用方式:

var adapter = ObjectAdapter(Adaptee())
adapter.method1()
adapter.method2()

类适配器与对象适配器的区别

类适配器使用的是继承的方式,直接继承了Adaptee,所以无法对Adaptee的子类进行适配。

对象适配器使用的是组合的方式,·所以Adaptee及其子孙类都可以被适配。另外,对象适配器对于增加一些新行
为非常方便,而且新增加的行为同时适用于所有的源。

基于组合/聚合优于继承的原则,使用对象适配器是更好的选择。但具体问题应该具体分析,某些情况可能使用类
适配器会适合,最适合的才是最好的。

接口适配器

接口需要实现的方法过多,使用不管有用没用都得实现,使用适配器先默认实现一些功能,只是实现需要实现的功能。

使用抽象类做一次中转实现的,具体方式这里不做介绍了。

来个在项目中的具体使用来看一下

class Task(num:Long):Callable<Long> {

    private var num = num

    override fun call(): Long {
        var r: Long = 0
        for (n in 1..num) {
            r += n
        }
        Log.v("========","Result: $r")
        return r
    }
}

在Task中计算和,这时想在一个Thread中运行这个功能。代码如下:

Callable<Long> callable = new Task(123450000L);
Thread thread = new Thread(callable); // compile error!
thread.start();

发现编译不过!因为Thread接收Runnable接口,但不接收Callable接口,肿么办?

一个办法是改写Task类,把实现的Callable改为Runnable,但这样做不好,因为Task很可能在其他地方作为Callable被引用,改写Task的接口,会导致其他正常工作的代码无法编译。

另一个办法不用改写Task类,而是用一个Adapter,把这个Callable接口“变成”Runnable接口,这样,就可以正常编译:

class RunnableAdapter(callable: Callable<Long>) : Runnable {
    var callable = callable

    override fun run() {
        try {
            callable.call()
        } catch (e: Exception) {

        }
    }
}
var callable = Task(1000L)
var thread = Thread(RunnableAdapter(callable))
thread.start()

运行结果:Result: 500500

顺便问个问题,我这个适配器模式是对象适配器模式还是类适配器模式呢?

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。