2016-06-27 1 views
1

Традиционная мудрость программирования, по-видимому, препятствует использованию статических методов в большинстве случаев. Часто у меня есть эти «менеджеры», например. UserManager, AppointmentManager e.t.c. Неизменно один из методов в менеджере - XXX getXXX(long xxxId), например. User getUser(long userId). Я действительно не понимаю, почему это не может быть статическим методом. Это похоже на фабричный метод (заводская схема a la GoF). Трудно пропустить удобство:Почему методы «getter» в классах «Менеджера» не статичны?

User user = UserManager.getUser(id);

и использовать

UserManager userManager = new UserManager(); User user = userManager.getUser(userId);

вместо этого.

P.S. Я верю в тестирование; Я просто не фанат-тестировщик, поэтому мне нужны причины, кроме насмешек.

+1

Это не то, что обычно называют геттерами. –

+0

Я знаю об этом. Однако я не мог найти лучшего имени. – Emmanuel

+1

.. и вместо этого используйте инъекцию зависимостей. 'new UserManager()' каждый раз, когда вы используете класс только для того, чтобы избежать «статических» методов, IMO - самый худший из них. – zapl

ответ

6

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

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

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

+0

Для экономии штата я с трудом могу себе представить, где мне это нужно, надеюсь, вы сможете немного разобраться. Для программирования интерфейса, возможно, это другой вопрос, потому что, цитируя Джошуа Блоха «Дизайн и документ для наследования или запрещающий его». В этих случаях я не ожидаю и не планирую наследование, поэтому кодирование интерфейса, если не что-то (в этих случаях), которое кажется актуальным. Наконец, хотя я не тестирую гений, единственные времена, когда я проверял свои тесты, гарантируя, что они были вызваны методами в указанном порядке, были тогда, когда я издевался. Вне этого, не совсем. – Emmanuel

+0

Для фабрики @Emmanuel Object, возможно, потребуется отслеживать объекты, которые были созданы через нее, таким образом, чтобы стать временным владельцем объектов, которые он создал до того, как объекты будут сохранены. Вы можете сохранить состояние несохраненных объектов во временном месте, когда ваше приложение завершает работу, чтобы вы могли восстановить его состояние при перезагрузке, не спрашивая конечных пользователей, хотите ли они сбросить или сохранить свою несохраненную работу при выходе. – dasblinkenlight

+0

@ Emmanuel Что касается программирования интерфейса, он не требует программирования для наследования. Это можно сделать просто, чтобы добавить степень изоляции между программистами, использующими фабрику, и программистами, поставляющими заводскую реализацию. Это полезно в ситуациях, когда несколько команд работают над различными частями проекта: вы настроили интерфейс заранее, а затем команды раздельно разрабатывают код, обеспечивающий и потребляющий интерфейс. – dasblinkenlight

1

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

В вашем случае у вас есть UserManager. Ни в коем случае полное приложение, не так ли? У вас может быть нечто более сложное, использующее UserManager. Предположим, у вас есть собственная версия StackOverflow (не делайте этого, конечно, stackoverflow является потрясающим, не нужно конкурировать).

Итак, у нас есть LoginService, который вызывает UserManager.getUser(). Это неизменная зависимость (поскольку мы не используем полиморфизм). Если UserManager.getUser() требует базовую базу данных SQL, то угадайте, что вам нужно для запуска (или проверки) LoginService .... базы данных SQL!

public class LoginService { 
    public boolean authenticate(String username, String password) { 
     User user = UserManager.getUser(username); // hard dependency on implementation 
     // other stuff 
    } 
} 

Более распространенное решение - абстрактные вещи, которые могут измениться за интерфейсом. Таким образом, вы можете поменять местами реализации. LoginService имеет работу, которая должна быть проверена и не должна зависеть от конкретной реализации базы данных.

public interface UserManager { 
    User getUser(String id): 
} 

public class SQLUserManager implements UserManager { 
    @Override 
    public User getUser(String id) { // SQL stuff } 
} 

class LoginService { 
    public LoginService(UserManager userManager) { 
     this.userManager = userManager; 
    } 

    public boolean authenticate(String username, String password) { 
     User user = userManager.getUser(username); 
     // other stuff 
    } 
} 

Теперь LoginService могут 1) быть испытаны независимо от того, что используется UserManager и 2) можно оставить в покое, если изменения реализации пользователь.

Это не насмешка, а тестирование ваших компонентов без установки всего пакета приложений.

0

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

Смежные вопросы