Маниакальный Бобер (bober_maniac) wrote,
Маниакальный Бобер
bober_maniac

Немного о программировании

Все-таки рефлексия - замечательная вещь. Она позволяет создавать абстрактные фабрики, пополнение которой объектами выполняется просто, удобно и понятно любому среднестатистическому быдлокодеру.

Сначала надо определить атрибут, который будет символизировать тот факт, что абстрактную фабрику надо пополнить возможностью создания объектов такого типа:
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false, Inherited=false)]
public class RegisterActionAttribute: Attribute
{
	public string ActionName { get; set; }

	public RegisterActionAttribute(string ActionName)
	{
		this.ActionName = ActionName;
	}
}


Затем - определить базовый абстрактный класс, который будет по совместительству фабрикой классов.
public abstract class Action


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

private static Dictionary<string, Type> _actionCache = new Dictionary<string, Type>();


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

public static Action CreateAction(string ID, Actor actor, params object[] additional)
{
	if (!_actionCache.ContainsKey(ID))
		throw new ArgumentOutOfRangeException("ID", ID, "Action with this ID is not found in the cache");

	List<Type> additionalTypes = new List<Type>();
	foreach (object current in additional)
		additionalTypes.Add(current.GetType());

	Type currentActionType = _actionCache[ID];
	ConstructorInfo cInfo = currentActionType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, Type.DefaultBinder, additionalTypes.ToArray(), new ParameterModifier[] { });
	if (cInfo == null)
		throw new ArgumentException(CreateConstructorNotFoundHelpInfo(currentActionType), "additional");

	Action newAction = (Action)cInfo.Invoke(additional);

	newAction._actor = actor;
	return newAction;
}


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

static Action()
{
	Assembly currentAssembly = Assembly.GetExecutingAssembly();
	Type[] AllInnerTypes = currentAssembly.GetTypes();
	foreach (Type current in AllInnerTypes)
		if (current.GetCustomAttributes(false).Any(c => Type.Equals(typeof(RegisterActionAttribute), c.GetType())))
		{
			RegisterActionAttribute aca = (RegisterActionAttribute)current.GetCustomAttributes(false).First(c => Type.Equals(typeof(RegisterActionAttribute), c.GetType()));
			if (current.IsSubclassOf(typeof(Action)))
				_actionCache.Add(aca.ActionName, current);
			else
				throw new ApplicationException(string.Format("Type {0} is registered as an Action, but it is not child of Action", current.FullName));
		}
}


Таким образом новый класс добавляется в кеш и становится доступным для нашей фабрики простым написанием кода, вроде такого
[RegisterAction("Move")]
public class MoveAction : Action
{
	public Point TargetPosition { get; private set; }

	private MoveAction(Point endPoint)
	{
		TargetPosition = endPoint;
	}
}


После чего, экземпляр создается еще проще
Action action = Action.CreateAction("Move", null, new Point(0, 0));


Все вышеперечисленное легко реализовать в виде snippet в MSVS, и если подобная архитектура бывает нужна - то пользоваться. Ну, а чтобы понять, когда она бывает нужна - смотрите шаблоны проектирования
Tags: программирование
Subscribe
  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your reply will be screened

    Your IP address will be recorded 

  • 7 comments