标签 博客 下的文章

Effective Java阅读笔记-item16

Dreamhead把他用大把银子买来的“Effective Java”借给我阅读,我真是很感动亚,我只能用行动来感谢Dreamhead了。^_^

16、接口优于抽象类
作者有一个观点就是:

* 接口是定义mixin(混合类型)的理想选择。

mixin的定义:一个类除了实现它的基本类型”primitive type”之外,还可以实现这个mixin类型,以表明它提供可供选择的行为,也就是说它允许将可选的功能混合到一个类的基本功能中。

作者举例Comparable就是一个mixin接口,这个接口提供的可选功能就是它的实例可以与其他的可相互比较的对象进行排序操作。

基于上面这一点,接口使得我们能够构造出非层次结构的类型框架。

什么是层次结构框架(class hierarchy)?
例如:Shape <– Rectangle <– Square这样一路继承下来,就是一个继承体系。

现在如果Shape假设并没有提供可比较的基本功能,而我们要在Square中实现可比较的功能,我们就可以在Square那加入一个旁支Comparable接口,让Square去实现它即可。

Shape <– Rectangle <– Square <– Square’s sub class
                            |
            Comparable <—–

上面蓝色部分就是一个class hierarchy,而Comparble恰恰就在这个class hierarchy上建立了一个非层次化的结构。

注:abstract class不能用来定义mixin,因为java不提供class的多重继承机制。

* abstract skeletal implementation class(抽象骨架实现类型)–结合抽象类和接口的优点,java中模拟多重继承

Interface缺点是无法拥有方法的实现。而抽象骨架实现的优点在于为抽象类提供实现上的帮助,也避免了抽象类作为类型定义时所特有的限制。

public abstract class AbstractMapEntry implements Map.Entry {
  // Primitives
  public abstract Object getKey();
  public abstract Object getValue();
  //  …
  // Implements the general contract of Map.Entry.equals
  public boolean equals(Object o) {
    if (o == this) return true;
    if (!o instanceOf Map.Entry))
      return false;
    Map.Entry arg = (Map.Entry) o;
   
    return eq(getKey(), arg.getKey()) &&
        eq(getValue(), arg.getValue());
  }
 
  // Since Object equals was overriden, we better override hashCode!
  public int hashCode() {
    return
      (getKey() == null ? 0: getKey().hashCode()) ^
      (getValue() == null ? 0: getValue().hashCode());
  }
}
书中提示:抽象骨架类专为继承而设计,所以要有详细的文档说明。

*抽象类的演化比接口的演化容易

我们举例说明这一点吧
Considering the following code:
//未演化前的代码:
public abstract class AbrBase{
    public void a();
    public void b();
};

public class Sub1 extends AbrBase{
    public void a(){
    }
    public void b(){
    }
};

public interface IBase{
    public void c();
    public void d();
};

public class Sub2 implements IBase{
    public void c(){
    }
    public void d(){
    }
};

//进化后代码
public abstract class AbrBase{
    public void a();
    public void b();
    public void e() {//为抽象类添加一新的具体的方法,注意抽象方法也不行
    }
};

public class Sub1 extends AbrBase{//在抽象类添加一具体方法后,子类可以不用改动
    public void a(){
    }
    public void b(){
    }
};

public interface IBase{
    public void c();
    public void d();
public void f(); //为接口添加一新的方法
};

public class Sub2 implements IBase{
    public void c(){
    }
    public void d(){
    }
    public void f(){ //子类必须修改实现新添加的方法,否则编译将不能通过
    }
};

解决办法:提供抽象骨架类
//进化之前代码
public interface IBase{
    public void c();
    public void d();
};

public abstract class AbrBase implements IBase{
    //primitives
    public void a();
    public void b();

    //implenments the method of IBase interface
    public void c(){
    }
    public void d(){
    }
};

public class Sub extends AbrBase{
    public void a(){
    }
    public void b(){
    }
//继承AbrBase对IBase的实现
};

进化后代码:
public interface IBase{
    public void c();
    public void d();
public void f(); //为接口添加一新的方法
};

public abstract class AbrBase implements IBase{
    //primitives
    public void a();
    public void b();

    //implenments the method of IBase interface
    public void c(){
    }
    public void d(){
    }
    public void f(){ //修改AbrBase以实现IBase新增加的method
    }
};
public class Sub extends AbrBase{//无需改变,继承超类对f()方法的实现
    public void a(){
    }
    public void b(){
    }
//继承AbrBase对IBase的实现
};
That’s all!^_^

Effective Java阅读笔记-item13、14

Dreamhead把他用大把银子买来的“Effective Java”借给我阅读,我真是很感动亚,我只能用行动来感谢Dreamhead了。^_^

13、支持不变性(immutable)

我感觉我们只需记住书中列出的几条规则:
1. 不要提供任何"可修改对象內容"的方法
2. 保证没有可被子类override的方法
3. 令所有field为 final
4. 令所有field都是 private
5. 保证对任何可变组件的互斥存取

Immutable object优点:
- Immutable object本质上是线程安全的,不需要同步
- Immutable object可以被自由的share
- Immutable object对于其他对象来说,形成了很多构件

缺点:对于每一个不同的值,都要形成一个独立的object
如:
String newStr = “AB”+”CD”;
这样可能会带来的是效率问题。

14、复合(composition)优先于继承(extends not implements)

在本Item中,作者针对安全继承提出几个观点:
- 包内继承很safe,因为super class和sub class都在一个Programmer的control下。
- 继承自专门为继承而设计并有很好的文档的类也很安全。

相反越界继承就是不安全的。

关于继承的几个观点:
- 继承打破的封装性;
- 当subclass确实是superclass的子类型的时候,也就是确实是“is a”的关系时才使用继承。

继承将会传播 superclass API 的所有缺陷,而复合允许你设计新的 API,隐藏 superclass 的缺陷。

书里还提到了composition vs delegete的概念,以前我对delegate的概念也不是很清楚,这次正好顺便好好分析一下:
以书中的代码为例:

public class InstrumentedSet implements Set {
    private int addCount = 0;
    … …
    public InstrumentedSet(Set s) {
        this.s = s;
    }
}

针对这段代码作者观点:“有时,复合(composition)和转发(forwarding)这两项技术的结合被错误地引用为“委托(delegation)” 从技术的角度而言,这不是委托(delegation),除非包装对象把自己(InstrumentedSet)传递给一个被包装的对象(Set)”

关于delegate:GOF那本书中如是说“Delegation is a way of making composition as powerful for reuse as inheritance [Lie86, JZ91]. In delegation, two objects are involved in handling a request: a receiving object delegates operations to its delegate. This is analogous to subclasses deferring requests to parent classes. But with inheritance, an inherited operation can always refer to the receiving object through the this member variable in C++ and self in Smalltalk. To achieve the same effect with delegation, the receiver passes itself to the delegate to let the delegated operation refer to the receiver 。

The main advantage of delegation is that it makes it easy to compose behaviors at run-time and to change the way they're composed”。

下面的代码改自某论坛上一个网友的代码,我自己觉得这段代码对于正确理解delegate很有帮助。
Considering the following code:

//MyDelegatee.java
public class MyDelegatee {
     void methodB() {
     }
}

//MyDelegate.java
public class MyDelegate {
   MyDelegatee delegatee;
   void methodA() {
       delegatee.methodB();
   }
}

上面的代码就不是delegate,而是composition和forwarding,因为MyDelegate直接调用了MyDelegatee object的方法,这只是forwarding methods。

//MyDelegatee.java
public class MyDelegatee {
     void methodB(MyDelegate delegate) {
        delegate.do();
     }
}

//MyDelegate.java
public class MyDelegate {
   MyDelegatee delegatee;
   void methodA() {
       delegatee.methodB(this);
   }
   void do(){
   }
}

MyDelegate已经把自身pass给了MyDelegatee,并且MyDelegatee调用了MyDelegate的方法,这是一种indirection。也就是说delegatee一定会调用delegate的某些方法,因此你首先得把delegate传递给delegatee。

我们在举个实际一点的例子,董事长和总经理的故事:
Considering the following code:
//Chairman.java
public class Chairman {
    private GeneralManager gm = new GeneralManager();

    public void doThroughGM(){
        gm.investmentDecisionMaking(this);
    }
    
    //董事长拥有的权利
    public void investmentDecisionMaking(){
        /*
         *董事长具有战略投资决策权,
         *董事长可将此权利授权给总经理
         */
         System.out.println("总经理被授权执行投资决策");
        
    }
    public static void main(String[] args) {
        Chairman chairman = new Chairman();
        chairman.doThroughGM();
    }
}

//GeneralManager.java
public class GeneralManager {
    public void investmentDecisionMaking(Chairman chairman){
        //总经理被授权获得的权利
        chairman.investmentDecisionMaking();
    }    
}
//output:
总经理被授权执行投资决策

上面的例子模仿了一个现实世界的过程,在现实世界中,假如董事长把权利授权给总经理,总经理一定会获取董事长才拥有的权利,它会利用这些权利来替公司做事。

举了这些例子后对delegate有些概念了吧^_^。

参考资料:
1、http://www.javaworld.com/javaworld/javaqa/2001-09/01-qa-0914-delegate.html
2、http://forum.javaeye.com/viewtopic.php?t=6120

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言精进之路1 Go语言精进之路2 Go语言编程指南
商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats