Полагаю, вы имеете в виду методы вместо функций? Вы действительно должны представить себе, что слово 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
помечает поля как неизменяемые ( по крайней мере в теории ).