源码地址:https://github.com/weilanhanf/PythonDesignPatterns
说明:
为了解决接口不兼容的问题引进一种接口的兼容机制,就是适配器模式,其通过提供一种适配器类将第三方提供的接口转换为客户希望的接口。生活中的例子例如:手机充电器要将220v的电源转换为手机适合的电压才能充电。这个充电器就起到了适配的作用。
适配器模式:将一个类的接口转换成客户希望的另一个接口。适配器模式让那些接口不兼容的类可以一起工作。适配器模式 别名为包装器(Wrapper)模式 定义中所提及的接口是指广义的接口,它可以表示一个方法或者方法的集合
适配器模式的结构
适配器模式包含以下3个角色: Target(目标抽象类) Adapter(适配器类) Adaptee(适配者类)
实例:
假设某公司A与某公司B需要合作,公司A需要访问公司B的人员信息,但公司A与公司B协议接口不同,该如何处理?先将公司A和公司B针对各自的人员信息访问系统封装了对象接口。
class ACpnStaff: """ A公司 """ name="" id="" phone="" def __init__(self,id): self.id=id def getName(self): print("A protocol getName method...id:%s"%self.id) return self.name def setName(self,name): print("A protocol setName method...id:%s"%self.id) self.name=name def getPhone(self): print("A protocol getPhone method...id:%s"%self.id) return self.phone def setPhone(self,phone): print("A protocol setPhone method...id:%s"%self.id) self.phone=phone class BCpnStaff: """ B公司 """ name="" id="" telephone="" def __init__(self,id): self.id=id def get_name(self): print("B protocol get_name method...id:%s"%self.id) return self.name def set_name(self,name): print("B protocol set_name method...id:%s"%self.id) self.name=name def get_telephone(self): print("B protocol get_telephone method...id:%s"%self.id) return self.telephone def set_telephone(self,telephone): print("B protocol get_name method...id:%s"%self.id) self.telephone=telephone class CpnStaffAdapter: """ C公司 """ b_cpn="" def __init__(self,id): self.b_cpn=BCpnStaff(id) def getName(self): return self.b_cpn.get_name() def getPhone(self): return self.b_cpn.get_telephone() def setName(self,name): self.b_cpn.set_name(name) def setPhone(self,phone): self.b_cpn.set_telephone(phone) #业务场景 if __name__=="__main__": acpn_staff=ACpnStaff("123") acpn_staff.setName("X-A") acpn_staff.setPhone("10012345678") print("A Staff Name:%s"%acpn_staff.getName()) print("A Staff Phone:%s"%acpn_staff.getPhone()) bcpn_staff=CpnStaffAdapter("456") bcpn_staff.setName("Y-B") bcpn_staff.setPhone("99987654321") print("B Staff Name:%s"%bcpn_staff.getName()) print("B Staff Phone:%s"%bcpn_staff.getPhone())
打印结果:
A protocol setName method...id:123
A protocol setPhone method...id:123
A protocol getName method...id:123
A Staff Name:X-A
A protocol getPhone method...id:123
A Staff Phone:10012345678
B protocol set_name method...id:456
B protocol get_name method...id:456
B protocol get_name method...id:456
B Staff Name:Y-B
B protocol get_telephone method...id:456
B Staff Phone:99987654321
可以看出A类与B类的内部方法不一样,也就是说无法使用一致的接口对A,B公司进行同时访问,那么C类就应运而生。C类扮演的就是协议和接口转化的适配器的角色,将B公司人员接口封装,而对外接口形式与A公司人员接口一致,达到用A公司人员接口访问B公司人员信息的效果。
模式优点
将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无须修改原有结构 增加了类的透明性和复用性,提高了适配者的复用性,同一个适配者类可以在多个不同的系统中复用 灵活性和扩展性非常好 类适配器模式:置换一些适配者的方法很方便 对象适配器模式:可以把多个不同的适配者适配到同一个目标,还可以适配一个适配者的子类
模式缺点 类适配器模式:
(1) 一次最多只能适配一个适配者类,不能同时适配多个适配者;(2) 适配者类不能为最终类;(3) 目标抽象类只能为接口,不能为类 对象适配器模式:在适配器中置换适配者类的某些方法比较麻烦
模式适用环境
系统需要使用一些现有的类,而这些类的接口不符合系统的需要,甚至没有这些类的源代码 创建一个可以重复使用的类,用于和一些彼此之间没有太大关联的类,包括一些可能在将来引进的类一起工作
另外:
适配器模式和装饰模式有一定的相似性,都起包装的作用,但二者本质上又是不同的,装饰模式的结果,是给一个对象增加了一些额外的职责,而适配器模式,则是将另一个对象进行了“伪装”。适配器可以认为是对现在业务的补偿式应用,所以,尽量不要在设计阶段使用适配器模式,在两个系统需要兼容时可以考虑使用适配器模式。