对“针对接口编程,而不是针对实现编程”的理解
今天在阅读《Head First设计模式》的时候,看到了这句话:“针对接口编程,而不是针对实现编程”,第一次见到的时候,不太清楚作者想表达的意思,想着到后来看看实例就懂了。没想到后面阅读时,发现作者反复提及这句话,我不得不停下来,仔细思考一下这句话的意义所在。
总结理解
其实“针对接口编程,而不是针对实现编程”这句话正是利用了Java语言中的多态。编程时针对超类型(父类)进行编程,也就是说变量的声明类型(或方法的返回类型)是超类型,而不是具体的某个子类。超类型中的各个方法的具体实现不在超类型中,而是在各个子类中。这样在程序执行时可以根据实际状况执行到真正的(某个子类)行为。这样带来的好处是,我们在声明一个变量时无需关心以后执行时的真正的数据类型是哪种(某个子类类型),这是种解耦合(松耦合)的思想。我们之后维护的时候可以随时将声明的变量替换为真正需要要执行的类型,具有很高的可维护性和可扩展性。所以其实我们还可以换个说法:“针对超类型编程”,超类型则通常是接口或是一个抽象类。这么说可能这还是比较抽象,我习惯性举个例子来感受下。
举例说明
场景需求
首先,假设我们有一个如下的场景需求:饲养场里面有几种动物,牛、猪和鸡。你现在带着你的小孩子过来,想让他感受下每个动物的叫声是啥样子的,于是你就有这样的一个需求,拉来一种动物,就听下它的叫声。
三种动物有一些共同特点,比如都有质量、都会叫、都会跑...我们现在可以先设计个Animal父类,此处我们只需要考虑叫声,所以简单构造如下(具体到每种动物叫声都不一样,所以做成接口形式,各个动物可以自己去实现):
- public interface Animal {
- ......
- public void makeSound();
- }
具体到“牛”,来单独实现其叫声方法makeSound():
- public class Cow implements Animal {
- @Override
- public void makeSound() {
- // TODO Auto-generated method stub
- System.out.println("哞哞~");
- }
- }
猪--Pig和鸡--Chicken与之类似,就不列举了。现在我们构造完成了,那么如何做到来一种动物,就听到其叫声呢?
针对实现编程
首先我们来“针对实现编程”,假设我们调用hearSound来听到每种动物的发声。现在,先来了一只牛,我们想听到其声音,可以把hearSound()编写如下:
- public void hearSound(Cow cow) {
- cow.makeSound();
- }
调用这个方法来发声: hearSound(new Cow());
然后又来了一只鸡,我们想听其声音,就需要再编写一个针对鸡的hearChickenSound:
- public void hearChickenSound(Chicken chicken) {
- chicken.makeSound();
- }
调用这个方法来发声: hearChickenSound(new Chicken());
这就是说,每想听到一种动物的声音,你就得去新建一个与该动物相关的hearSound()方法,原来的方法没法复用,因为你已经在原来的方法里写死了只能是“牛”发声。每一个hearSound()方法与每种动物紧耦合,扩展起来不方面(可能每个heardSound方法都得去扩展相应的功能)。而且,假如我原来是只想听“牛”叫,就写个hearSound(Cow cow)方法就行了。现在不想听了,只想听“鸡”叫,那么就得修改掉hearSound()方法,还有曾经所有调用过hearSound()方法的也需要进行相应的修改,可见这种设计维护起来也很差劲。
针对接口编程
既然上述“针对实现编程”有诸多问题,就得寻找解决方式,“针对接口编程”的好处也就显现出来。我们来看看上述场景需求下,“针对接口编程”如何来实现。
Animal类还是与上述一样的,每种动物还是各自实现其makeSound()方法。不同的是,在设计hearSound()方法的时候,我们的参数设计成Animal接口,而不是具体的某种动物(牛、鸡或猪):
- public void hearSound(Animal animal) {
- animal.makeSound();
- }
这样,来了一头“牛”,我们可以这样调用: hearSound(new Cow()); ,
继续来了一只鸡,我们还可以这样调用: hearSound(new Chicken()); 。
由于hearSound()方法定义的时候调用的是接口,我们无需关心以后执行时的真正的数据类型是哪种。而在实际调用时,我们可以传入实现了Anima接口的任意一种子类(牛、鸡或猪),而且hearSound()中调用的makeSound()方法也是真正传入的类型的makeSound()方法。这种松耦合的设计理念提高了代码的复用度,需要扩展的时候也很方便。想在替换原有类型的时候也很方便,提高了可维护性。
对“针对接口编程,而不是针对实现编程”的理解的更多相关文章
- ZT 针对接口编程而不是针对实现编程
java中继承用extends 实现接口用 implements 针对接口编程而不是针对实现编程 2009-01-08 10:23 zhangrun_gz | 分类:其他编程语言 老听说这句,不知道到 ...
- 针对接口编程能帮助达到面向对象开发和设计中"低耦合"的要求. 某公司...打印机...(笔试中遇到的题目)
针对接口编程能帮助达到面向对象开发和设计中"低耦合"的要求. 举个例子:某公司有一台特殊打印机,还可以使用一年,一年后可能换为另一种打印机,这两种打印机都特殊而贵. ...
- [一] java8 函数式编程入门 什么是函数式编程 函数接口概念 流和收集器基本概念
本文是针对于java8引入函数式编程概念以及stream流相关的一些简单介绍 什么是函数式编程? java程序员第一反应可能会理解成类的成员方法一类的东西 此处并不是这个含义,更接近是数学上的 ...
- struts2针对mvc的框架 spring针对解耦与事务的框架
struts2针对mvc的框架 spring针对解耦与事务的框架
- 设计模式之Programming to an Interface, not anImplementation 程序指向接口,而不是实现
Class inheritance is basically just a mechanism for extending an application's functionality by reus ...
- python学习之flask接口开发,环境变量扩展,网络编程requests
python基础 flask之mock接口 所谓mock接口,其实就是我们在正式接口还没联调或者是测试接口没有正式使用时,自己创建一个模拟接口,来供项目暂时打通功能或者测试流程梳理的桥梁,而我们这儿使 ...
- 我面向 Google 编程,他面向薪资编程
面试官:同学,说一说面向对象有什么好处? 神仙开发者:我觉的面向对象编程没有什么好处. 面试官:为什么(摊手.问号脸)? 神仙开发者:因为在面向对象的时候,我对象总是跟我说话,问我在淘宝上挑的衣服哪个 ...
- [.net 面向对象编程基础] (2) 关于面向对象编程
[.net 面向对象编程基础] (2) 关于面向对象编程 首先是,面向对象编程英文 Object-Oriented Programming 简称 OOP 通俗来说,就是 针对对象编程的意思 那么问 ...
- 学习响应式编程 Reactor (1) - 响应式编程
响应式编程 命令式编程(Imperative Programing),是一种描述计算机所需做出的行为的编程范式.详细的命令机器怎么(How)去处理以达到想要的结果(What). 声明式编程(Decla ...
随机推荐
- 在OpenStack虚拟机实例中创建swap分区的一种方法
测试组里一个同学负责MapR的搭建,MapR文档中建议每个节点上至少有24GB的swap分区,不知道MapR为啥会有这种反人类的建议……swap无非就是一块顺序读写的磁盘空间,莫非省着内存不用,用sw ...
- etcd使用之ttl不准确问题
问题现象 部署有一个etcd集群,分别是10.8.65.106,10.8.65.107和10.8.65.108. 然后我使用etcdctl为一个值设置ttl,然后通过watch观察,发现失效时间不准确 ...
- 【转】HTTP Response Header 的 Content-Disposition
因为听到有同事讨论JSP输出Excel文件的,就是在页面上有一个[导出]按钮,能够将查询结果导出到Excel文件让用户下载.有人说要用POI在后台生成临时的Excel文件,然后通过读取FileStre ...
- Spring MVC---数据绑定和表单标签
数据绑定和表单标签 数据绑定 数据绑定是将用户输入绑定到领域模型的一种特性,在Spring MVC的controller和view数据传递 ...
- js jquery 实现html页面之间参数传递(单一参数、对象参数传递)
最近自己在忙着做毕业设计,后台程序员,前端菜鸡,因为需要,所以实现了html页面之间参数传递.------jstarseven .菜鸡的自我修养. 页面A代码如下: <!DOCTYPE html ...
- 《linux内核完全剖析》笔记03-进程创建
根据一下问题来看笔记 进程占多大的线形地址空间 进程实际分配多少物理内存 创建进程的开销在哪里 一. 从fork系统调用开始 kernel/sys_call.s第222行 _sys_fork: cal ...
- 结构-行为-样式-angularJs笔记
0.关于Ng-app 通过ngApp指令来引导Angularjs应用是一种简洁的方式 ,适合大多数情况.在高级开发中,例如使用脚本装载应用,您也可以使用Bootstrap手动引导AngularJs ...
- MVC源码解析 - UrlRoutingModule / 路由注册
从前面篇章的解析, 其实能看的出来, IHttpModule 可以注册很多个, 而且可以从web.config注册, 可以动态注册. 但是有一个关键性的Module没有讲, 这里就先来讲一下这个关键性 ...
- 手机端H5 header定义样式
<meta content="width=device-width,initial-scale=1.0, maximum-scale=1.0, user-scalable=0" ...
- IOS 成员变量,全局变量,局部变量定义,static与extern的区别
IOS 成员变量,全局变量,局部变量定义,static与extern的区别 1,先说定义 1)成员变量定义:生存与该类的生命周期,变量存活周期跟你定义的该类实体对象一样:作用域是整个实体对象:可以在h ...