Ваша интуиция довольно хороша тем, что ваши классы действительно очень тесно связаны друг с другом. Я собираюсь сослаться на Принципы объектно-ориентированного проектирования Боба Мартина .
Следуя принципу единой ответственности , вы хотите убедиться, что вам не нужно обновлять оба этих класса, если один из них изменится. Сейчас оба класса слишком много знают друг о друге.
Мы можем начать с EditMapUtilites
и изменить рефакторинг доступа к обработчику сеанса, чтобы более точно передать намерение. В основном, состояние приложения ограничено пользователем. Кроме того, мы можем изменить способ доступа к этим значениям, чтобы сделать их менее связанными, используя методы, которые достигают той же цели.
public class EditMapUtlities
{
//Omitting the wireup details for now
private SessionHandler ApplicationState {get; set;}
public static Boolean isInitialEditMapPageLoad
{
get { return ApplicationState.GetValue<Boolean>("EditMapInitialPageLoad"); }
set { ApplicationState.SetValue("EditMapInitialPageLoad", value); }
}
// REST OF CLASS NOT GERMAIN TO DISCUSSION AND OMITTED
}
И класс SessionHandler
теперь будет содержать два новых метода:
public static T GetValue<T>(String key)
{
return currentSession[key] ?? default(T);
}
public static void SetValue(String key, Object val)
{
currentSession[key] = val;
}
Хорошо, теперь один класс немного более многословен, но ваш класс сеанса стал проще и более гибким. Добавить дополнительные значения просто, но вы можете спросить себя, почему бы просто не использовать прямой сеанс?
Причина, по которой мы реструктурировали классы таким образом, заключалась в том, чтобы приблизить нас на один шаг к чистому разрыву между ними. Получив доступ к нашему классу SessionHandler
через свойство, мы можем теперь сделать наш рефакторинг еще на один шаг и использовать Принцип инверсии зависимости .
Вместо того, чтобы полагаться на конкретную реализацию SessionHandler
, почему бы нам не абстрагировать хранение переменных Application State за интерфейсом. Тот, который выглядит так:
public interface IApplicationState
{
public T GetValue<T>(String key);
public SetValue(String key, Object val);
}
Теперь мы можем просто изменить наше свойство внутри EditMapUtlities
, чтобы использовать интерфейс, и передать его через конструктор с помощью хорошего старого ручного внедрения зависимостей.
public class EditMapUtlities
{
//Omitting the wireup details for now
private IApplicationState ApplicationState {get; set;}
public EditMapUtilities(IApplicationState appState)
{
if(appState == null) throw new ArgumentNullException("appState");
ApplicationState = appState;
}
public static Boolean isInitialEditMapPageLoad
{
get { return ApplicationState.GetValue<Boolean>("EditMapInitialPageLoad"); }
set { ApplicationState.SetValue("EditMapInitialPageLoad", value); }
}
// REST OF CLASS NOT GERMAIN TO DISCUSSION AND OMITTED
}
Вы можете легко реорганизовать свой класс SessionHandler
, чтобы реализовать интерфейс и не быть статичным:
public SessionHandler: IApplicationState { //Implementation }
Или, если вам нужно поддерживать обратную совместимость с другими областями кода, вы можете создать класс-оболочку и оставить SessionHandler статическим:
public SessionHandlerWrapper: IApplicationState
{
//Call SessionHandler from within this class
}
Теперь верно, что вам придется вручную создавать конкретный экземпляр IApplicationState
и передавать его в EditMapUtilities
, но теперь ни один класс не зависит от другого. Фактически, если вы когда-нибудь захотите сохранить это в базе данных в будущем, вам просто придется написать новый класс, который реализует IApplicationState
. Только вызывающий код должен быть изменен, чтобы использовать новый класс базы данных вместо сеансового.
Теперь вы можете наслаждаться своей новой архитектурой LooSley и минимизированными изменениями, которые вам придется внести, чтобы добавить новые функции.