`
TonyLian
  • 浏览: 401547 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

优先使用接口而不是类

阅读更多

正在研读Joshua Bloch的《Effective Java》一书。书中至少在两条中提到了“优先使用接口而不是类”,一是第25条中的“参数类型优先使用接口而不是类”;另一个是第34条中的“通过接口引用对象”。

 

两条中所提及的例子一个是Map,一个是List(因为此书的作者就是Collection Framwork的作者)

 

Map ht = new Hashtable();

 

String value= doSomething(ht);

 

private String doSomething(Map pram) {

    .....

    // 比如这里需要 pram.clone(); 该怎么办呢?

    Map pram2 = pram.clone();   // 会出错的,因为Map接口中没有clone方法,那是Cloneable接口中的

}

 

 

书上说“没有理由在编写一个方法时使用Hashtable作为输入,相反,应该使用Map”。

 

但是我想问的是:一个类可能实现多个接口,如果不同接口中所定义的方法,都要用到,那么该选择哪个接口作为“代表”呢?恐怕哪个都是片面的。比如,如果我要在方法对传入的参数做clone操作,那么就必须使用Hashtable作为参数了,因为它还实现了Cloneable接口。

 

可以说这种时候,就是一个充分的理由,使用类(Hashtable)来声明了吗??

 

分享到:
评论
7 楼 TheMarine 2009-05-27  
恩,的确如楼主所说的,如果这样的话使用实现类是最恰当的。毕竟也只是说优先于类,并没有说必须是接口。事实上问题就在于接口到底是干什么的,依我的理解应该就是约束动作的。那么这样说来,跨栏运动员=人+能跨栏,问题也就变成既然人和能跨栏都有了,有必要为跨栏运动员再做一个接口?我之所以说hashmap这个例子不太好是因为haspmap没有实际的生活意义(或者说不便理解,当然也是可以说的通的),而且重要的是它已经是实现类了,它并没有再去抽象一层出来,因为我们通常说参加跨栏比赛的是跨栏运动员,而不是能跨栏的人,这样的思维最自然最oo,因为无论是人或者能跨栏接口的实现都不能代表跨栏运动员,因此设计的时候可能会做一个跨栏运动员的抽象,再细分为不同性别不同赛程的运动员:public class 110栏 implements 跨栏运动员 而不是 public 110栏 extends 人 implements 能跨栏,这里多了一层抽象就自然多了(跨栏运动员才是人和能跨栏的抽象,110栏是更具化的东西,不应该直接去继承或实现),对于参加跨栏比赛这个方法(楼主的设计只能使用具体类),这个时候传递跨栏运动员接口优于传递类才是joshua bloch 的初衷的吧。这个是我的拙见。当然对于楼主这个特定的例子,我还是表示赞同。
6 楼 TonyLian 2009-05-27  
楼上讲的很明白,我一开始也有这样但稍微模糊的认识。
但是,‘碰巧’API中已经为我们设计好了“飞机”和“可垂直起降的机器”,
而我要用到对象的 垂直起飞() 方法,也要用到 驾驶员席位数 域,
也就是说,如果一个 “名为垂直起降飞机” 的类,继承了 “飞机” 这个抽象类,同时实现了“可垂直起降的机器”这么个接口,那不正合适吗。
但当作为参数传递时,显然只用“可垂直起降的机器”这个接口是不行的。

public class 鹞式战斗机 extends 飞机 implements 可垂直起降的机器

就像 public class Hashtable extends Dictionary implements Map

当然 Hashtable 还实现诸如 Cloneable 等几个接口,如我一楼的例子,
如果我的方法中需要用到 values() 方法(实现Map接口),又要用到 keys() 方法 (实现Dictionary抽象类),甚至还要用到 clone() 方法 (实现Cloneable接口)

例子已经很清楚了,按照楼上几位的说法。在设计之初就该考虑我需要一个什么样的接口,而不是什么样的实现。

那么,就如果用战斗机那个例子,很明显,你会告诉,先写一个“垂直起降飞机”或“垂直起降战斗机”这么个接口(是不是抽象类也可以呀?)

但是,如果就是 Hashtable Map Dictionary Cloneable 这个例子呢?你不会建议我(为API追加)重写一个 三合一的 新接口吧??
解决方案又是什么呢? 只能硬着头皮 用 Hashtable 做参数了吗??
5 楼 TheMarine 2009-05-27  
这样讲吧,你的设计不合理.
你需要使用参数对象的功能,但你的参数类型没有提供该功能(换句话说你的类没有保证被实现特定方法),合适么?必须提供具有该功能的对象才行.这里hashmap是'碰巧'实现了map接口,但是你的方法内部确实需要一个hashmap类型的对象,而不是一个map对象.这里拿jdk api来说不合适,不知道你听懂我的意思了么,大概以你的理解应该有个hashmap接口你就想通了?使用子类扩展功能的时候其实它的继承意义已经变化了.垂直起落飞机是飞机,但是用到垂直起落功能的时候,它是飞机但是更始"可以垂直起落的机器".既然方法内部更重视垂直起落这个词,那我就应该传入具有垂直起落飞机,而不是飞机.
4 楼 zhuxing 2009-05-22  
TonyLian 写道
楼上两位,clone只是我举的一个例子。可能是这个例子不好。

按照 silentlakeside 的说法,思考顺序反了,我也是赞同的。

最先考虑的应该是我需要一个什么样的功能(接口),然后是实现它。

但,如果一开始的设计时,就知道我要使用的两个功能是不搭边的,它们怎么看也是要被两个接口定义的,这时该如何呢? 不能因为要在一个方法中使用,就把它们都定义在一个接口中呀。而且,如果很明显,API中已经有这样的接口了,它们也是分别的两个,如何呢?总不能自己在重写一个,把API中的两个接口合二为一吧。

“最先考虑的应该是我需要一个什么样的功能(接口),然后是实现它。”


要多想、多分析,否则接口容易沦为一个单纯实现层面的东西,接口的产生可能就不是严肃了。

很多时候,你需要某一个东西,这个东西不存在,你可以去创造,当然,也多去想想可不可以由已有的东西转化过来。
3 楼 TonyLian 2009-05-22  
楼上两位,clone只是我举的一个例子。可能是这个例子不好。

按照 silentlakeside 的说法,思考顺序反了,我也是赞同的。

最先考虑的应该是我需要一个什么样的功能(接口),然后是实现它。

但,如果一开始的设计时,就知道我要使用的两个功能是不搭边的,它们怎么看也是要被两个接口定义的,这时该如何呢? 不能因为要在一个方法中使用,就把它们都定义在一个接口中呀。而且,如果很明显,API中已经有这样的接口了,它们也是分别的两个,如何呢?总不能自己在重写一个,把API中的两个接口合二为一吧。
2 楼 rdcyash 2009-05-21  
clone is not a good method

maybe you will like Collections.unmodifiedMap(pram)
1 楼 silentlakeside 2009-05-21  
1. 这个例子不合适,Map可以使用其他方法克隆一份,例如取出所有的kye/value组装成一个新的Map

2. 思考问题的顺序有问题。不应该从具体类的角度考虑选择哪个接口作为代表,而是首先考虑使用该类的方法需要用到哪个接口,然后让那个具体类去实现该接口,如果该具体类无法变更(例如是java api的类),则自己重新封装一个。

相关推荐

    C#抽象类和接口的区别.docx

    4. **组合优于继承**:在设计时,优先考虑对象组合(组合复用),而不是过多地使用继承,以避免类层次过深导致的问题。 总之,抽象类和接口都是C#中实现抽象和多态的重要手段,它们各有优势,适用于不同的场景。在...

    面向接口编程详解

    因此,在选择使用接口还是抽象类时,应考虑是否需要共享具体实现以及类层次结构的复杂程度。 2. **面向接口编程与面向对象编程的关系**:面向接口编程不是一种独立于面向对象编程之外的全新编程范式,而是面向对象...

    关于抽象类和接口的两篇不错文章

    - 接口主要用于定义对象的行为规范,强调“做什么”而不是“怎么做”。 #### 四、总结 虽然抽象类和接口都能实现多态性和抽象性,但它们各自适用于不同的场景。选择使用抽象类还是接口取决于设计的需求。当需要...

    优先队列-java可以选择属性和升序降序

    虽然优先队列不是线程安全的,但你可以使用迭代器安全地遍历队列中的元素: ```java Iterator<Integer> iterator = queue.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); } ...

    priority_queue_1.0_excitingfbh_matlab优先队列_matlab优先队列_优先队列_

    4. **自定义类**:像"priority_queue_1.0"这样的库可能就是通过创建自定义的MATLAB类来封装这些操作,提供一个简洁的接口供用户使用。 在"priority_queue_1.0"这个压缩包中,可能包含了实现上述功能的MATLAB源代码...

    如何在Java中消除实现继承和面向接口编程

    - **优先考虑组合而非继承**:通过组合其他类的对象来实现功能,而不是继承。例如,可以用一个`Motor`对象组合到`Car`类中,而不是让`Car`继承`Motor`。 - **使用策略模式**:当行为的改变不涉及类的基本性质时,...

    八数码问题宽度优先搜索(Java实现)

    在具体实现过程中,可以使用`java.util.Queue`接口的实现,如`LinkedList`,来实现队列操作,而`HashMap`则提供了高效的键值对存储和查找功能。此外,还需要考虑优化哈希表的冲突处理策略,以提高查找效率。 【文件...

    SQLite 3的C∕C++ 接口

    这些接口不是教程性质的文档,而是提供了一种精确的参考手册,目的是帮助开发者理解如何使用 SQLite。对于初学者来说,建议首先阅读官方提供的快速入门指南和 C/C++ 接口介绍。 #### 接口分类 SQLite 接口根据稳定...

    Java——多态与接口.rar

    而方法覆盖是子类对父类已有方法的重新定义,当子类对象调用同名方法时,会优先执行子类的方法,而不是父类的。这使得子类可以在不改变父类原有行为的基础上扩展功能。为了实现覆盖,方法必须满足以下条件:同名、同...

    类设计原则

    以及合成/聚合复用原则(Composite/Aggregation Reuse Principle, CARP),优先使用对象组合或聚合,而不是继承,以增强软件的灵活性和可维护性。 这些原则共同构成了面向对象设计的基础,有助于创建出更易于理解、...

    java面向对象编程、类的封装、接口和基本类库练习题.doc

    15. **继承和多态**:Java支持单一继承,即一个类只能直接继承自一个父类,但可以通过接口实现多继承的特性。 选择题涉及的知识点: 1. 对象是属性和方法的封装体,不是所有对象都有继承性,如原始类型没有继承性...

    Java面向对象程序设计--继承与接口.pptx

    成员变量的隐藏发生在子类中声明了与父类同名的变量时,子类会优先使用自己定义的变量,而隐藏父类的变量。方法重写(Override)则发生在子类提供与父类相同名称和签名的方法,以便为特定的子类行为提供不同的实现。...

    Qt class designing rule(Qt类设计规范)

    在设计类结构时,应优先考虑使用组合(has-a)关系,而不是继承(is-a)。继承可能导致类层次过于复杂,而组合则更加灵活,可以更自由地组合对象的功能,同时避免了多继承带来的问题。 ### 4. 单一职责原则 (Single...

    WebControl接口使用文档1

    - 注意:该接口可能不是最佳选择,建议优先考虑`getCardNo()`。 7. **对IC卡加密** - 函数原型:`int EncryptICCard(QString strKey)` - 描述:用于对IC卡进行加密处理。 - 参数: - `strKey`: 加密使用的密钥...

    java开发面向对象原则

    例如,在 Java 中,可以使用接口来定义一个方法,而不是使用具体的实现类。这样,当需要改变实现时,只需要改变接口的实现,而不需要改变客户端代码。 二、优先使用组合而非继承(Favor Composition Over ...

    Java中类与类之间的关系

    通常建议优先考虑使用组合而不是继承,原因有几点: - **封装性**:使用组合可以更好地保持类之间的封装性,因为每个类都专注于自己的职责。 - **灵活性**:组合提供了更高的灵活性,因为可以轻松地替换或修改组件...

    【05-面向对象(下)】

    •一个类可以实现一个或多个接口,继承使用extends关键字,实现接口则使用implements关键字。 实现接口 •一个类实现了一个或多个接口之后,这个类必须完全实现这些接口里所定义的全部抽象方法(也就是...

    面向对象的设计原则——类设计原则

    在业务设计中,这意味着应该优先考虑在接口或抽象类中定义业务逻辑,具体的实现细节留给子类或具体的实现类去完成。这样一来,即使具体的实现发生改变,只要保持接口不变,高层模块的代码就不需要修改。 综上所述,...

    Lock接口与synchronized关键字

    4. **公平性选择**:Lock接口的实现(如`ReentrantLock`)允许开发者选择是否使用公平锁策略,公平锁可以确保线程按照请求顺序获取锁,而非公平锁则可能优先考虑已经在等待的线程。 #### 七、总结 - **...

    设计模式之依赖倒转原则

    - 在设计系统时,优先考虑接口或抽象类,而非具体实现。 - 使用接口而非实现进行参数传递和返回值,以减少耦合。 - 尽可能让类之间的关系通过构造函数或配置文件来确定,而不是硬编码。 总结起来,依赖倒转原则...

Global site tag (gtag.js) - Google Analytics