Вопрос о природе унаследованных классов Java - PullRequest
3 голосов
/ 12 августа 2010

Так что я думаю, что у меня есть довольно простой вопрос.Скажем, есть Java-программа с открытым исходным кодом com.cow.moo, которую вы включаете в свой проект com.bee.buzz.

У moo есть классные классы, большинство из которых вы не хотите трогать,но есть пара, которую вы делаете.Теперь, на данный момент, лучше всего расширить классы, которые вы хотите изменить, верно?(Я знаю, что было много сказано о расширениях и реализациях, но ни один из этих классов не является интерфейсом, так что об этом не может быть и речи.)

Мой вопрос, скажем, что это класс в moo:

package com.cow.moo;
public class Milk {
    private float currentMilk;
    public int getMilk() { /* Stuff */ }
    public float convertToGallons (float liquid) { /* More Stuff */ }
}

Теперь, скажем, я хочу просто использовать getMilk в моем новом классе, расширяющем Milk.Тем не менее, getMilk в Milk использует приватные переменные (например, currentMilk) и другие функции, которые я не буду включать (например, convertToGallons.) Нужно ли будет включать эти другие переменные и функции, если я хочу, чтобы моя новая функция работала правильно?Я не хочу сильно изменять функцию, просто добавлю немного к ней.Каков наилучший способ сделать это?

Полезны также советы по построению более крупного проекта.Я полагаю, что некоторым экспертам по Java здесь не понадобится пяти секунд, чтобы найти ответ.Спасибо за ваше время.

Ответы [ 5 ]

6 голосов
/ 12 августа 2010

Общая рекомендация - отдавать предпочтение композиции по сравнению с наследованием .

Скажем, у вас есть интерфейс и существующая реализация, которая в основном соответствует вашим потребностям, например

public interface MilkProvider { public float getMilk(); }
public class Milk implements MilkProvider { // same as you example }

и нужна другая пользовательская реализация, вы можете написать ее так:

public class MyMilk implements MilkProvider {
  private MilkProvider milk; 

  public MyMilk(int someValue) {
    milk = new Milk(someValue);  // unfortunatly we can't get rid of a depencency
                                 // to the concrete class because we need to create
                                 // something. An existing factory could help, but
                                 // but usually there's none implemented.
  }

  public float getMilk() {
    float result = milk.getMilk();
    // do somethink with the result
    return float;
  }
}
1 голос
/ 12 августа 2010

Теперь, скажем, я хочу просто использовать getMilk в моем новом классе, расширяющем Milk. Однако getMilk в Milk использует закрытые переменные (например, currentMilk) и другие функции, которые я не буду включать (например, convertToGallons.) Должен ли я включать эти переменные и функции, если я хочу, чтобы моя новая функция работала корректно?

Вам не нужно будет включать public функции и переменные. Основная концепция наследования заключается в том, что в качестве подкласса все открытые (и защищенные) члены вашего родительского класса включаются в ваш подкласс бесплатно. Таким образом, ваш подкласс (скажем, HoneyMilk) может звонить convertToGallons с самого начала.

Переопределение getMilk в этом случае намного сложнее, поскольку оно опирается на закрытую переменную (к которой ваш подкласс не может получить доступ). Мой совет - переключить ваше мышление с того, чтобы относиться к классу как к «белому ящику», к «черному ящику». Под этим я подразумеваю, что вам следует реализовать переопределенную версию getMilk так, как будто вы не смогли увидеть исходный код Милка. Хотя это может показаться обходным решением (я имею в виду, почему я не могу просто настроить эту строку здесь ?!), это заставит вас реализовать свой подкласс, используя только то, что родительский класс предоставляет публично. Это также сильно подчеркивает важность абстракции, которую крайне важно использовать при разработке крупномасштабных проектов.

1 голос
/ 12 августа 2010

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

0 голосов
/ 12 августа 2010

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

0 голосов
/ 12 августа 2010

Вы можете расширять класс и обращаться к переменным экземпляра с помощью методов доступа (методов получения и установки), если они общедоступны.

Вы можете использовать AOP (Аспектно-ориентированное программирование) , чтобы изменить классы moo во время выполнения без изменения его источников.

Считайте, что вы слишком много читаете Композиции и темы наследования .

Надеюсь, это поможет вам.

...