#c# #oop
#c# #ооп
Вопрос:
У меня возникли некоторые проблемы с рефакторингом кода. У меня есть класс appcache, в котором есть статический конструктор, который инициализирует некоторую информацию и создает кеш. Он также имеет другие методы, такие как getobject, saveobject…
Я создаю новый класс кэша, который должен наследовать все полезные методы appcache. Я также хочу, чтобы у этого нового класса кэша был статический конструктор, но вместо раздела «clusterclient», который является разделом подключения, другая инициализация.
public class AppCache
{
internal static AWSCache _internalCache;
static AppCache()
{
TimeSpan awsExpiration = TimeSpan.FromMinutes(int.TryParse(ConfigurationManager.AppSettings["AppCacheMinutes"], out var cacheMinutes) ? cacheMinutes : 60);
TimeSpan localExpiration = TimeSpan.FromMinutes(int.TryParse(ConfigurationManager.AppSettings["LocalCacheMinutes"], out cacheMinutes) ? cacheMinutes : 2);
_internalCache = new AWSCache("clusterclient", awsExpiration, localExpiration, null);
}
// OTHER USEFUL METHODS ...
public class OtherCache : AppCache
{
static OtherCache()
{
TimeSpan awsExpiration = TimeSpan.FromMinutes(int.TryParse(ConfigurationManager.AppSettings["AppCacheMinutes"], out var cacheMinutes) ? cacheMinutes : 60);
TimeSpan localExpiration = TimeSpan.FromMinutes(int.TryParse(ConfigurationManager.AppSettings["LocalCacheMinutes"], out cacheMinutes) ? cacheMinutes : 2);
_internalCache = new AWSCache("otherclusterclient", awsExpiration, localExpiration, null);
}
}
// код в других частях программы
AppCache.Вызовы GetObject(key) -> _internalCache.GetObject(…, …, …);
Мой главный вопрос в основном заключается в том, как мне настроить и реорганизовать класс AppCache, чтобы мне не приходилось полностью копировать его и создавать новый класс с именем OtherCache. Вместо этого я хочу наследовать «ДРУГИЕ ПОЛЕЗНЫЕ МЕТОДЫ», а также статически инициализировать _internalCache по-другому.
Я устал от разных вещей и не мог заставить это работать. Он всегда вызывает статический метод базового класса и инициализирует _internalcache таким образом, даже когда я использую, как показано ниже.
OtherCache.GetObject(ключ), который вызывает => _internalCache .SaveObject(…, …, …);
Комментарии:
1. Вы обнаружили некоторые из причин, по которым вы не должны делать ничего из этого в статическом конструкторе. Статические конструкторы должны быть предельно простыми и выполнять очень мало работы. Если у вас сложная логика инициализации, поместите ее в метод. Тем не менее, трудно понять, о чем вы здесь просите. Не могли бы вы сформулировать вопрос более конкретно?
2. Поэтому, когда я использую класс OtherCache. Например, конструктор производного класса инициализирует _internalCache, а затем AppCache также инициализирует его правильно? Или я ошибаюсь? _internalCache не создается с использованием определенного мной connectionSection («otherclusterclient»), вместо этого он создается с помощью «clusterclient», который мне не нужен, когда я вызываю. GetObject с использованием класса OtherCache. Если это имеет смысл
3. Статические конструкторы имеют очень много нюансов в способе их инициализации и могут привести к множеству странных проблем в лучшие времена. Похоже, вы хотите
Lazy<T>
или просто метод инициализации4. Поскольку
_internalCache
является статическим, он будет общим для всех типов, производных отAppCache
. Вы не получите отдельные экземпляры_internalCache
дляAppCache
класса и его производных классов — вы получите только один экземпляр кэша для всех из них. Если вы планируете использовать обаAppCache
и производный класс одновременно, этот конфликт необходимо разрешить. Либо выигрывает одна инициализация кэша, либо вам необходимо выполнить рефакторинг для хранения нескольких кэшей, и каждый производный тип должен извлекать правильный кэш.
Ответ №1:
Вот реализация, которая (я думаю) соответствует вашим пожеланиям по синтаксису. Он использует шаблон Singleton, базовый класс для скрытия / повторного использования деталей реализации и дженерики с отражением для ввода уникальной информации об инициализации.
Вы упомянули, что вы можете реорганизовать AppCache
класс, и этот подход требует его модификации.
Singleton: шаблон Singleton обеспечивает требуемый статический доступ (избегая статических конструкторов). Этот шаблон также гарантирует наличие отдельных кэшей для каждого производного типа, где каждый кэш может быть инициализирован уникальными строками по вашему желанию. Я реализовал Singleton с использованием Lazy, хотя есть и другие варианты.
Базовый класс: наличие базового класса позволяет вам определять все статические «ДРУГИЕ ПОЛЕЗНЫЕ МЕТОДЫ» в базе для хорошего повторного использования кода. Я начал один для вас. Это работает при условии, что каждый производный кэш всегда имеет тип AWSCache
.
Обобщения / отражение: в частности, IMO, он использует обобщения с where
предложением и, в конечном счете, отражение (через активатор.CreateInstance), чтобы найти и вызвать производный конструктор и инициализировать кэши так, как вы хотите. Это означает, что каждому производному классу нужно определить только одну строку: конструктор, который вызывает базовый конструктор и выдает ему уникальную строку, используемую при инициализации кэша. Отражение в целом может быть медленным, но в этом случае оно вызывается только один раз при инициализации синглтона.
Представлено как консольное приложение для компиляции:
using System;
using System.Configuration;
namespace SomeNamespace
{
public class Program
{
static void Main()
{
// example of desired syntax - no initialization required.
_ = AppCache.GetObject("key");
_ = DerivedCache.GetObject("key");
}
}
// note the 'where' clause.
// we want to restrict T to always be derived from this class type, and nothing else.
public abstract class CacheBase<T> where T : CacheBase<T>
{
private readonly AWSCache _internalCache;
protected CacheBase(string cacheTypeName)
{
TimeSpan awsExpiration = TimeSpan.FromMinutes(int.TryParse(ConfigurationManager.AppSettings["AppCacheMinutes"], out var cacheMinutes) ? cacheMinutes : 60);
TimeSpan localExpiration = TimeSpan.FromMinutes(int.TryParse(ConfigurationManager.AppSettings["LocalCacheMinutes"], out cacheMinutes) ? cacheMinutes : 2);
_internalCache = new AWSCache(cacheTypeName, awsExpiration, localExpiration, null);
}
// very, VERY simplistic implementation of the Singleton pattern
// sets the 'nonPublic' bool to 'true' to find the hidden constructors
// you may want better error handling of instance creation; modify as needed
protected static readonly Lazy<T> _lazy = new Lazy<T>(() => (T)Activator.CreateInstance(typeof(T), true));
// implement all the "USEFUL METHODS" here, in the base class.
// This works because all derived classes create a cache of the same type.
private static AWSCache Cache => _lazy.Value._internalCache;
public static object GetObject(string key)
{
return Cache.GetObject(key);
}
// etc.
}
// for derived classes, remember to keep the constructors private to maintain Singleton pattern.
public class AppCache : CacheBase<AppCache>
{
private AppCache() : base("clusterCache") { }
}
public class DerivedCache : CacheBase<DerivedCache>
{
private DerivedCache() : base("otherClusterCache") { }
}
// implemented as a shell so things compile for simple console app with no references
public class AWSCache
{
public AWSCache(string name, TimeSpan awsExpiration, TimeSpan localExpiration, object dunno) { }
public object GetObject(string key) { return null; }
}
}
Использование выглядит чистым, как я думаю, вы хотите. Вы можете вызвать DerivedClass.USEFULMETHOD()
в любое время.
Каждый производный класс всегда должен использовать себя в качестве универсального типа T
. Вы могли бы (и должны) добавить некоторую обработку ошибок, чтобы выявить случай, когда разработчик вводит неправильный тип T
.
Комментарии:
1. Спасибо за подробный ответ. Это соответствует моим потребностям. Я ценю это. Я точно не знаю, почему я получаю downvoted. Может быть, это название, но, опять же, очень ценю его.
2. @YANS Я не голосовал против, но я предполагаю, что это связано с формулировкой и представлением вопроса. Понижение может использоваться для выражения того, что цель вопроса неясна, что затрудняет ответ. Я действительно думаю, что текст вопроса можно улучшить (даже заголовок), поскольку я должен был догадаться о ваших намерениях.
3. Понял, в следующий раз буду писать более аккуратно.