Класс с единственным методом - лучший подход?

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

// Initialize arguments in constructor
MyClass myObject = new MyClass(arg1, arg2, arg3);
myObject.myMethod();

// Pass arguments to method
MyClass myObject = new MyClass();
myObject.myMethod(arg1, arg2, arg3);

// Pass arguments to static method
MyClass.myMethod(arg1, arg2, arg3);

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

Ответ 1

Мне нравились классы полезности, заполненные статическими методами. Они сделали большую консолидацию вспомогательных методов, которые в противном случае лежали бы вокруг, создавая избыточность и поддерживая ад. Они очень просты в использовании, не создаются, не уничтожаются, просто не срабатывают. Я предполагаю, что это была моя первая нечаянная попытка создания сервис-ориентированной архитектуры - множество служб без гражданства, которые только что сделали свою работу, и ничего больше. Однако, когда система растет, драконы будут приближаться.

Полиморфизм
Скажем, у нас есть метод UtilityClass.SomeMethod, который радостно гудит. Внезапно нам нужно немного изменить функциональность. Большая часть функциональности одна и та же, но мы должны изменить пару частей, тем не менее. Если бы это не был статический метод, мы могли бы сделать класс деривации и изменить содержание метода по мере необходимости. Поскольку это статический метод, мы не можем. Конечно, если нам просто нужно добавить функциональность до или после старого метода, мы можем создать новый класс и называть его старым, но это просто грубо.

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

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

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

Параметр ползучести
Во-первых, этот маленький милый и невинный статический метод может принимать один параметр. По мере роста функциональности добавляется пара новых параметров. Вскоре добавляются дополнительные параметры, которые являются необязательными, поэтому мы создаем перегрузки метода (или просто добавляем значения по умолчанию на языках, которые их поддерживают). Вскоре у нас есть метод, который принимает 10 параметров. Только первые три действительно необходимы, параметры 4-7 являются необязательными. Но если параметр 6 указан, необходимо заполнить 7-9... Если бы мы создали класс с единственной целью сделать то, что сделал этот статический метод, мы могли бы решить это, взяв требуемые параметры в конструктор и позволяет пользователю устанавливать необязательные значения через свойства или методы для одновременного задания нескольких взаимозависимых значений. Кроме того, если метод вырос до такого уровня сложности, он, скорее всего, должен быть в своем собственном классе.

Требование потребителей к созданию экземпляра классов без причины
Один из наиболее распространенных аргументов заключается в следующем: зачем требовать, чтобы потребители нашего класса создавали экземпляр для вызова этого единственного метода, хотя впоследствии он не использовался для экземпляра? Создание экземпляра класса - очень дешевая операция на большинстве языков, поэтому скорость не является проблемой. Добавление лишней строки кода для потребителя - это низкая стоимость заложить основу для гораздо более удобного решения в будущем. И, наконец, если вы хотите избежать создания экземпляров, просто создайте одноэлементную оболочку своего класса, которая позволяет легко повторное использование, хотя это и делает требование о том, чтобы ваш класс не имел гражданства. Если он не является апатридом, вы все равно можете создавать статические методы-обертки, которые обрабатывают все, сохраняя при этом все преимущества в долгосрочной перспективе. Наконец, вы также можете создать класс, который скрывает экземпляр, как если бы он был одноэлементным: MyWrapper.Instance - свойство, которое просто возвращает новый MyClass();

Только ситы совершают сделки в абсолютах

Конечно, есть исключения из моей неприязни к статическим методам. Истинные классы полезности, которые не представляют опасности для вздутия, являются отличными случаями для статических методов - например, System.Convert. Если ваш проект является одноразовым, без требований к будущему обслуживанию, общая архитектура действительно не очень важна - статическая или не статическая, на самом деле не имеет значения - скорость разработки, однако.

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

Ответ 2

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

Классы, которые существуют только для своих методов, должны оставаться статическими.

Ответ 3

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

Ответ 4

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

Я также предостерег бы от гигантского класса Utils, который имеет десятки несвязанных статических методов. Это может стать дезорганизованным и громоздким в спешке. Лучше иметь много классов, каждый из которых имеет несколько связанных методов.

Ответ 5

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

Ответ 6

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

Ответ 7

Я бы предположил, что его трудно ответить на основе предоставленной информации.

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

Конечно, трудно сказать, почему вам нужно создать один класс только для этого одного метода. Является ли это типичной ситуацией класса "Утилиты", поскольку большинство из них предполагает? Или вы внедряете какой-то класс правил, чего может быть больше в будущем.

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

Ответ 8

Может ли ваш класс быть статическим?

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

Ответ 9

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

Ответ 10

Для простых приложений и помощников internal я бы использовал статический метод. Для приложений с компонентами я люблю Managed Extensibility Framework. Вот выдержка из документа, который я пишу, чтобы описать шаблоны, которые вы найдете в моих API.

  • Услуги
    • Определяется интерфейсом I[ServiceName]Service.
    • Экспортируется и импортируется по типу интерфейса.
    • Единая реализация предоставляется хост-приложением и потребляется внутри и/или с помощью расширений.
    • Методы сервисного интерфейса являются потокобезопасными.

Как надуманный пример:

public interface ISettingsService
{
    string ReadSetting(string name);

    void WriteSetting(string name, string value);
}

[Export]
public class ObjectRequiringSettings
{
    [Import]
    private ISettingsService SettingsService
    {
        get;
        set;
    }

    private void Foo()
    {
        if (SettingsService.ReadSetting("PerformFooAction") == bool.TrueString)
        {
            // whatever
        }
    }
}

Ответ 11

Я бы просто сделал все в конструкторе. так:

new MyClass(arg1, arg2, arg3);// the constructor does everything.

или

MyClass my_object(arg1, arg2, arg3);

Ответ 12

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

Вы должны обратить внимание на состояние системы.

Ответ 13

Возможно, вы сможете избежать ситуации вместе. Попробуйте рефакторировать, чтобы получить arg1.myMethod1(arg2, arg3). Swap arg1 с arg2 или arg3, если это имеет смысл.

Если у вас нет контроля над классом arg1, то украсьте его:

class Arg1Decorator
    private final T1 arg1;
    public Arg1Decorator(T1 arg1) {
        this.arg1 = arg1;
    }
    public T myMethod(T2 arg2, T3 arg3) {
        ...
    }
 }

 arg1d = new Arg1Decorator(arg1)
 arg1d.myMethod(arg2, arg3)

Обоснование заключается в том, что в ООП обрабатываются данные и методы обработки данных. Кроме того, вы получаете все преимущества, о которых упоминал Марк.

Ответ 14

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