Вопрос

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

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

Это было полезно?

Решение

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

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

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

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

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

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

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

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

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

Другие советы

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

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

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

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

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

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

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

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

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

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

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

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

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

Если этот метод не имеет состояния и вам не нужно его передавать, то имеет смысл определить его как статический.Если вам ДЕЙСТВИТЕЛЬНО необходимо передать метод, вы можете рассмотреть возможность использования делегат а не один из других предложенных вами подходов.

Для простых приложений и internal помощники, я бы использовал статический метод.Для приложений с компонентами мне нравится Платформа управляемой расширяемости.Вот отрывок из документа, который я пишу, чтобы описать шаблоны, которые вы найдете в моих 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
        }
    }
}

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

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

или

MyClass my_object(arg1, arg2, arg3);

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top