国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

Effective Java

2019-11-14 22:43:20
字體:
來源:轉載
供稿:網友
Effective java - 慎用tagged class

作者的原標題是<PRefer class hierarchies to tagged classes>,即用類層次優于tagged class。

我不知道有沒有tagged class這么一說,其實作者指的tagged class的是一個類描述了多種抽象,可以根據某個field決定不同的實例。下面是書中例子,使用shape和部分表示長度的field構成形狀并計算面積,腦補一下什么是tagged class:

class Figure {    enum Shape {        RECTANGLE, CIRCLE    };    // Tag field - the shape of this figure    final Shape shape;    // These fields are used only if shape is RECTANGLE    double length;    double width;    // This field is used only if shape is CIRCLE    double radius;    // Constructor for circle    Figure(double radius) {        shape = Shape.CIRCLE;        this.radius = radius;    }    // Constructor for rectangle    Figure(double length, double width) {        shape = Shape.RECTANGLE;        this.length = length;        this.width = width;    }    double area() {        switch (shape) {        case RECTANGLE:            return length * width;        case CIRCLE:            return Math.PI * (radius * radius);        default:            throw new AssertionError();        }    }}

不難看出這個類想傳達什么信息,也不難看出這樣的方式有很多缺陷。雖然能看懂是什么意思,但由于各種實現擠在一個類中,其可讀性并不好。不同的實現放到一個類里描述,即會根據實現的不同而使用不同的field,即,field無法聲明為final。(難道要在構造器里處理不用的field?)雖然微不足道,內存確實存在毫無意義的占用。不夠OO。

雖然上一篇把類層次說得一無是處,其實類層次就是用來解決這一問題的,而上面的tagged class是用非OO的方式模仿類層次。將tagged class轉為類層次,首先要將tagged class里的行為抽象出來,并為其提供抽象類。以上面的Figure為例,我們之需要一個方法——area。接下來需要為每一個tag定義具體子類,即例子中的circle和rectangle。然后為子類提供相應的field,即circle中的radius和rectangle的width、length。最后為子類提供抽象方法的相應實現。其實都不用這樣去說明轉換步驟,因為OO本身就是很自然的東西。

轉換結果如下:

abstract class Figure {    abstract double area();}class Circle extends Figure {    final double radius;    Circle(double radius) {        this.radius = radius;    }    double area() {        return Math.PI * (radius * radius);    }}class Rectangle extends Figure {    final double length;    final double width;    Rectangle(double length, double width) {        this.length = length;        this.width = width;    }    double area() {        return length * width;    }}class Square extends Rectangle {    Square(double side) {        super(side, side);    }}

這樣做的好處顯而易見,代碼簡單清晰,沒有樣板代碼;類型相互獨立,不會受到無關field的影響,field可以聲明為final。子類行可以獨立進行擴展,互不干擾。

回到最初的tagged class,它真的就一無是處?如果使用這個類,我只需要在調用構造器時使用相應的參數就可以得到想要的實例。就像策略模式那樣。當然,tagged class也許可能算是策略模式(傳遞的應該是某個行為的特征,而不是實例特征),但策略模式在Java中并不是這樣使用。

通常,一個策略是通過調用者通過傳遞函數來指定特定的行為的。但Java是沒有函數指針的,所以我們用對象引用實現策略模式,即調用該對象的方法。對于這種僅僅作為一個方法的"載體",即實例等同與方法指針的對象,作者將其稱為函數對象(function object)。

舉個例子,比如我們有這樣的一個具體策略(concrete strategy):

class StringLengthComparator {    private StringLengthComparator() {    }    public static final StringLengthComparator INSTANCE = new StringLengthComparator();    public int compare(String s1, String s2) {        return s1.length() - s2.length();    }}

具體策略的引用可以說是一個函數指針。對具體策略再抽象一層即成為一個策略接口(strategy interface)。對于上面的例子,java.util中正好有Comparator:

public interface Comparable<T> {    int compare(T o1, T o2);    boolean equals(Object obj);}

于是,我們使用的時候可能會用匿名類傳遞一個具體策略:

Arrays.sort(stringArray, new Comparator<String>() {    public int compare(String s1, String s2) {        return s1.length() - s2.length();    }});

用代碼描述似乎是那么回事,我只是在我想使用的地方傳遞了一個具體策略。缺點很明顯——每次調用的時候又需要創建一個實例。但又能怎么做? 把每個可能用到的具體策略在某個Host類里實現策略接口后都做成field并用final聲明?感覺很傻,但確實可以考慮,因為這樣做還有其他好處。比如,相比匿名類,具體策略可以有更直觀的命名,而且具體策略可以實現其他接口。

代碼如下:

// Exporting a concrete strategyclass Host {    private static class StrLenCmp        implements Comparator<String>, Serializable {            public int compare(String s1, String s2) {                return s1.length() - s2.length();            }    }    // Returned comparator is serializable    public static final Comparator<String> STRING_LENGTH_COMPARATOR = new StrLenCmp();    ... // Bulk of class omitted}

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 德格县| 醴陵市| 张家口市| 广饶县| 安吉县| 班戈县| 满洲里市| 昭苏县| 枣强县| 瓮安县| 盐亭县| 高雄县| 民丰县| 兴安县| 沈丘县| 林甸县| 黄陵县| 利津县| 边坝县| 彰化县| 濉溪县| 德阳市| 满洲里市| 永安市| 阳山县| 同德县| 县级市| 新建县| 社旗县| 西昌市| 常德市| 广东省| 乐昌市| 大庆市| 故城县| 兴山县| 阿图什市| 那坡县| 河间市| 西贡区| 阳信县|