Как инкапсулировать вспомогательные функции (обычно используемые apis) в язык ООП, такой как java? - PullRequest
3 голосов
/ 21 марта 2010

Эти функции могут быть трудно вписать в определенный класс,

Как лучше всего с ними бороться?

Ответы [ 7 ]

4 голосов
/ 21 марта 2010

Полагаю, вы имеете в виду методы вместо функций? Вы действительно должны представить себе, что слово static не существует в мире Java, на самом деле не существует случая, когда static действительно принесет вам пользу в долгосрочной перспективе.

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

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

Чтобы решить эту проблему, я бы предложил вам создать иерархию объектов, к которой вы обращаетесь к своим вспомогательным методам, вызывая myUtil.someGroup().someMethod(param1, param2); Это на самом деле так, как работают некоторые API, например, популярная веб-инфраструктура Apache Wicket настраивается с помощью Параметры объекта, который использует состав , чтобы позволить себе иметь несколько групп различных функций.

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

public class ImageUtil {

    public static BufferedImage adjustHue(float difference,
                                          BufferedImage original) {
        /* ... */
        return adjusted;
    }

    public static BufferedImage adjustBrightness(float difference,
                                                 BufferedImage original) {
        /* ... */
        return adjusted;
    }

    public static BufferedImage adjustContrast(float difference,
                                               BufferedImage original) {
        /* ... */
        return adjusted;
    }

    public static BufferedImage setHeight(int newHeight,
                                          BufferedImage original) {
        /* ... */
        return adjusted;
    }

    public static BufferedImage setWidth(int newWidth,
                                         BufferedImage original) {
        /* ... */
        return adjusted;
    }

}

вместо этого у вас должны быть эти интерфейсы для описания каждого набора операций

public interface IImageColorOperations {

    BufferedImage adjustHue(float difference, BufferedImage original);

    BufferedImage adjustBrightness(float difference, BufferedImage original;)

    BufferedImage adjustContrast(float difference, BufferedImage original);

}

public interface IImageDimensionOperations {

    BufferedImage setHeight(int newHeight, BufferedImage original);

    BufferedImage setWidth(int newWidth, BufferedImage original);

}

и сопровождающий отдельный класс для каждого интерфейса, который вы создаете в своем классе служебных программ "main" image, вот так

public class ImageUtils {

    private final IImageDimensionOperations dimension;
    private final IImageColorOperations color;

    public ImageUtils() {
        this(new ImageDimensionOperations(),
             new ImageColorOperations());
    }

    /**
     * Parameterized constructor which supports dependency injection, very handy
     * way to ensure that class always has the required accompanying classes and
     * this is easy to mock too for unit tests.
     */
    public ImageUtils(IImageDimensionOperations dimension,
                      IImageColorOperations color) {
        this.dimension = dimension;
        this.color = color;
    }

    /* utility methods go here */

}

Но подождите, это еще не все! Теперь есть два пути, вы можете решить для себя, какой из них вы хотели бы выбрать.

Сначала , вы можете использовать композицию для непосредственного предоставления методов интерфейса:

public class ImageUtils implements IImageDimensionOperations,
                                   IImageColorOperations {

    private final IImageDimensionOperations dimension;
    private final IImageColorOperations color;

    public ImageUtils() {
        this(new ImageDimensionOperations(),
             new ImageColorOperations());
    }
    /* etc. */
}

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

Ваш второй вариант заключается в непосредственном представлении самих классов операций (, поэтому я добавил туда эти finals! ):

public class ImageUtils {

    public final IImageDimensionOperations dimension;
    public final IImageColorOperations color;

    public ImageUtils() {
        this(new ImageDimensionOperations(),
             new ImageColorOperations());
    }    

    public ImageUtils(IImageDimensionOperations dimension,
              IImageColorOperations color) {
        this.dimension = dimension;
        this.color = color;
    }

    /* Nothing else needed in this class, ever! */

}

делая это, вы получаете такие приятные на вид звонки, как

BufferedImage adjusted = imageUtils.color.adjustHue(3.2f, original);

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

2 голосов
/ 21 марта 2010

В зависимости от назначения этих вспомогательных функций, вы можете взглянуть на каждый Apache Commons . Если это, например, функции, которые должны удалить java.lang.* связанный шаблонный код, то взгляните на Commons Lang (Javadocs здесь ). То же самое существует для java.io.* шаблона в виде Commons IO (Javadocs здесь ) и java.sql.* от DbUtils (Javadocs здесь ).

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

2 голосов
/ 21 марта 2010

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

1 голос
/ 21 марта 2010

Я бы порекомендовал создать класс Util и сбросить любую статическую функцию, которая никуда не годится. Вы все равно должны разделить функции на группы в зависимости от того, для чего они используются. Например, API GUI, API базы данных и т. Д. *

1 голос
/ 21 марта 2010

Обычной практикой является создание класса со статическими методами.

Я предпочитаю не делать этого, а создавать экземпляр класса и затем вызывать методы для него. Зачем ? Потому что тогда я могу настроить этот экземпляр по-разному. Чтобы сделать это с помощью статических методов, потребуется передать настройку при каждом вызове метода.

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

0 голосов
/ 22 марта 2010

Вам следует взглянуть на Google Collections и Guava , поскольку они имеют некоторые функции, которые обычно предоставляются в виде статических методов в полном стиле OO, например

0 голосов
/ 21 марта 2010

Первое, что нужно сделать, конечно же, это попытаться найти (подходящее) место для них вписаться в структуру класса.

Но если их нет, не рожать, создайтетретий класс с помощниками.Для этого есть две основные стратегии:

Первая стратегия использует статические методы:

public class FooHelper
{
    public static Bar doSomething(Foo) {
    }
    /* ... */
}

// usage:
Bar b = FooHelper.doSomething(fooInstance);

Другая во многом такая же, но в экземпляре класса:

public class FooHelper
{
    public Bar doSomething(Foo) {
    }
    /* ... */
}

// usage:
FooHelper fh = new FooHelper();
Bar b = fh.doSomething(fooInstance);

Иногда вы видите шаблон Singleton, чтобы получить экземпляр, но только когда-либо имеете один экземпляр:

public class FooHelper
{
    private static FooHelper theInstance = new FooHelper();

    public static FooHelper getInstance() {
        return theInstance;
    }

    public Bar doSomething(Foo) {
    }
    /* ... */
}

// usage:
FooHelper fh = FooHelper.getInstance();
Bar b = fh.doSomething(fooInstance);

(Обратите внимание, что это упрощенная реализация Singleton, какая из реализаций Singleton, вероятно, подходитдля помощников. Это не подходит для всех классов Singleton.)

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...