2016-05-30 6 views
9

В минувшие выходные я прочитал кое-что о интерфейсах, абстрактных классах и принципах дизайна. В конце я немного запутался, и я попытался построить пример того, что я узнал (или думал, что узнал).Класс расширяет абстрактные классы. Интерфейс

Вот мой пример: В этом случае будет модель для класса, содержащего информацию о деревьях.

Прежде всего я хотел бы сделать интерфейс:

public interface Tree{ 
    public void grow(); 
} 

Интерфейс содержит все методы, которые должны быть реализованы в конкретных деревьев. Пока что так хорошо, но для такого дерева нужны некоторые атрибуты (переменные), которые распределяются по всем семействам деревьев. Для этой цели я хотел бы использовать абстрактный класс:

public abstract class AbstractTree implements Tree { 
    private String barColor; 
    private int maxHeight; 
    private boolean isEvergreen; 
} 

Является ли это правильный путь, или я не в состоянии сделать вид договора о атрибуты (переменные), которые должны быть в других классах?

После того, как часть атрибута выполнена, я хотел бы иметь 3 типа деревьев.

  • Дуб
  • Maple
  • Spruce

Таким образом, каждый из этих деревьев "tpyes" может иметь индивидуальные переменные.

public class OakTreeImpl extends AbstractTree{ 
    private String barColor; 
    private int maxHeight; 
    private boolean isEvergreen; 
    private String foo; 
    @Override 
    public void grow() { 
    } 
} 

Подходит ли этот подход к объектно-ориентированным принципам дизайна или я совершенно не согласен с ним?

+3

AbstractTree следует использовать 'protected' поля, так что вам не нужно переопределить их в реализации –

+0

почему бы не использовать абстрактный класс только вместо этого? – VedX

+0

Я объявляю их защищенными в реферате и удаляю их из Impl-класса? И я все еще могу получить доступ (получить/установить) значения переменных, даже если я не вижу их непосредственно в классе? – user2742409

ответ

4

Хотя это может быть частично субъективным, я должен согласиться с другими ответами, данными до сих пор.

Интерфейс пользователя TreeNOT устаревшее. Если вы хотите моделировать Tree, тогда должен быть интерфейс Tree, в котором четко указаны методы, которые есть у каждого Tree.

В частности, я рекомендовал бы просто заменить его классом AbstractTree. Некоторые люди говорят, что вы вряд ли должны использовать абстрактные классы вообще (например, Jaroslav Tulach in "Practical API Design"). Я бы хотя бы сказал, что вы должны использовать их очень консервативно. Самое главное: вы должны стараться избегать появления абстрактных классов в публичном интерфейсе других классов. Например, если у вас был еще один класс/интерфейс, с помощью метода, как

void makeGrow(Tree tree) { 
    System.out.println("Growing "+tree); 
    tree.grow(); 
} 

затем заменить этот вид Tree к AbstractTree уменьшится гибкость. Вы никогда не сможете использовать класс, который не наследует от AbstractTree - и учитывая, что вы можете наследовать только один класс, это может быть серьезным ограничением. (Вы всегда можете реализовать несколько интерфейсов - поэтому интерфейс не ограничивает здесь гибкость).

Но даже если вы используете абстрактный байт класс, я бы рекомендовал использовать поля protected консервативно. Или, в более общем плане, знать о последствиях наследования от класса, как описано в «Пункт 17 - Дизайн и документ для наследования или запретить его» от "Effective Java" by Joshua Bloch.

Во многих случаях вы не хотите, чтобы класс наследования имел полный доступ к полям. Поэтому вы должны хотя бы рассмотреть возможность создания полей private и предлагать только protected методы для доступа, которые вы хотите предоставить наследующим классам.

public interface Tree{ 
    public void grow(); 
} 

abstract class AbstractTree implements Tree { 

    // Do the values of these fields ever change? If not, 
    // then make them final, and set them only in the 
    // constructor 
    private final String barColor; 
    private final int maxHeight; 
    private final boolean evergreen; 

    protected AbstractTree(...) { ... } 

    // Subclasses are only allowed to read (but not write) these fields 
    protected final String getBarColor() { return barColor; } 
    protected final intgetMaxHeight() { return maxHeight; } 
    protected final boolean isEvergreen() { return evergreen; } 
} 
+0

Ваша прокламированная философия дизайна не противоречит моему утверждению. Однако я считаю, что если у вас есть абстрактный класс для деревьев, вы не должны создавать отдельный интерфейс, который не только называется «Tree», но будет использоваться только абстрактным древовидным классом. Это не имеет никакого смысла! Интерфейс полезен только, если вы не знаете, кто его реализует, или ему нужна эта дополнительная гибкость.Единственная причина, по которой мы вообще используем абстракцию, заключается в том, что вы не должны создавать экземпляр нового дерева без указания того, какой тип (Maple, Oak и т. Д.). – 000000000000000000000

+1

@ 000000000000000000000 * «... но будет использоваться только абстрактным деревом» * - Я думаю, что в этом смысл: что вы хотите передать методу, который получает дерево? Вы хотите перейти в 'AbstractTree' (который, несмотря на имя, является * специфическим *, потому что он должен расширять' AbstractTree'), или вы хотите иметь возможность проходить в любом «Дереве», независимо от того, как это реализовано? (I.e. независимо от того, основана ли эта * реализация * («интерфейса»!) На «AbstractTree» или нет)? Опять же, это может быть частично субъективным, и мой подход может выглядеть как «overengineering», но я предпочитаю интерфейсы здесь – Marco13

+1

(И, между прочим: я не совсем понял, почему вы думаете, что может быть оправдание для интерфейса «Plant» , но не для интерфейса 'Tree'. Это просто вопрос о том, какой должен быть« корень »концептуальной иерархии, и я не вижу, как это защищает * от * моделирования« дерева »как интерфейса) – Marco13

6

Я предпочел бы отмечать переменные экземпляра как protected.

Поскольку все защищенные члены суперкласса доступны для дочерних классов. Если и только если, родительские и дочерние классы в одном пакете

public abstract class AbstractTree implements Tree { 
    protected String barColor; 
    protected int maxHeight; 
    protected boolean isEvergreen; 
} 

public class OakTreeImpl extends AbstractTree{ 

    // I can access barColor, maxHeight, isEvergreen in this class 

    @Override 
    public void grow() { 

    } 
} 
+0

. Вы совершенно правы, но я думаю, что ответ 000000000000000000000 подходит для моего не очень хорошо заданного вопроса. Но за усилия. – user2742409

7

Это работает, однако это не имеет особого смысла, поскольку интерфейс полностью устарел в этом случае.
Вы должны добавить метод вырасти до своего AbstractTree так:

public abstract class AbstractTree{ 
    protected String barColor; 
    protected int maxHeight; 
    protected boolean isEvergreen; 

    public abstract void grow(); 
} 

Используя интерфейс будет иметь смысл, если вы хотите иметь различные виды растений, которые все должны быть в состоянии расти, например.

interface Plant{ 
    void grow(); 
} 

abstract class Tree implements Plant{ 
    void grow(){ /* do sth */ } 
} 

abstract class Flower implements Plant{ 
    void grow(){ /* do sth totally different */ 
} 

Целью интерфейса является обеспечение той же методы в различных классах с различными реализациями, в то время как абстрактный класс предоставляет методы и атрибуты, которые являются общими во всех своих дочерних классах.
Если метод абстрактного класса также является абстрактным, каждый дочерний класс должен реализовать его самостоятельно.

+0

Большое спасибо, теперь я получаю это, надеюсь: D – user2742409

Смежные вопросы