Patrick Skelton Ответов: 4

Как использовать дженерики с заводским шаблоном?


Может ли кто-нибудь, пожалуйста, сказать мне, где я ошибаюсь со следующим кодом? (Я надеюсь, что ответ не "везде".)

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

	public abstract class LogicBase {}
	public class LogicSubclassA : LogicBase
	{
		public string LogicSubclassAOnly => "LogicA";
	}
	public class LogicSubclassB : LogicBase
	{
		public string LogicSubclassBOnly => "LogicB";
	}

	public abstract class GraphicsBase<T> where T : LogicBase
	{
		public GraphicsBase( T logicObject ) => LogicObject = (T)logicObject;
		public T LogicObject { get; }
	}

	public class GraphicsSubclassA : GraphicsBase<LogicSubclassA>
	{
		public GraphicsSubclassA( LogicSubclassA logicObject ) : base( logicObject ) {}
		public void SomeFunction()
		{
			Console.WriteLine( LogicObject.LogicSubclassAOnly );
		}
	}

	public class GraphicsSubclassB : GraphicsBase<LogicSubclassB>
	{
		public GraphicsSubclassB( LogicSubclassB logicObject ) : base( logicObject ) {}
		public void SomeFunction()
		{
			Console.WriteLine( LogicObject.LogicSubclassBOnly );
		}
	}


// Please see below the code for the specific compiler error.
	public static class GraphicsObjectFactory
	{
		public static GraphicsBase<LogicBase> CreateInstance( string specificType )
		{
			switch( specificType )
			{
				case "A": return new GraphicsSubclassA( new LogicSubclassA() );
				case "B": return new GraphicsSubclassB( new LogicSubclassB() );
				default: throw new ArgumentException();
			}
		}
	}


Ошибка такова: не удается неявно преобразовать тип GraphicsSubclassA в GraphicsBase<LogicBase>

Что я уже пробовал:

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

Этот вопрос является продолжением предыдущего вопроса, который я опубликовал здесь: предварительный вопрос

CHill60

Следуйте дальше или нет, какова ваша конкретная проблема?
Edit - заметил проблему как комментарий в вашем коде, но "не компилировать" не помогает - какова точная ошибка, о которой сообщается?

Patrick Skelton

Извините - я (надеюсь) улучшил вопрос.

4 Ответов

Рейтинг:
9

BillWoodruff

к вашему сведению: в коллекции DoFactory design-pattern Collection (коммерческое программное обеспечение) есть несколько хороших учебников (бесплатно): проверьте их tut on the Factory Pattern: [^примечание: Я купил их программное обеспечение несколько лет назад и многому научился с его строго формальным последовательным содержанием ... и стилем.

То, что вы делаете, кажется слишком сложным: посмотрите, дает ли вам этот код какие-либо идеи.

использование:

Dog dog = AnimalFactory.NewAnimal<Dog>("Mangy");

Cat cat = AnimalFactory.NewAnimal<Cat>("Mouser");
код для AnimalFactory:
using System;

namespace YourFactory
{
    public interface IAnimal<T>
    {
        string Name { get; set; }
    }

    public class Dog: IAnimal<Dog>
    {
        public Dog(string name)
        {
            Name = name;
        }

        public string Name { get; set; }
    }

    public class Cat: IAnimal<Cat>
    {
        public Cat(string name)
        {
            Name = name;
        }

        public string Name { get; set; }
    }

    public static class AnimalFactory
    {
        public static T NewAnimal<T>(string name) where T : IAnimal<T>
        {
            return (T) Activator.CreateInstance(typeof(T), name);
        }
    }
}


Patrick Skelton

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

BillWoodruff

Я рад, что вы нашли это полезным.

Рейтинг:
2

Richard MacCutchan

Снеш Праджапати[^] написал хороший набор статей в Заводские Узоры - Простой Заводской Узор[^].


Рейтинг:
1

RickZeeland

Может быть, это будет полезно: Общий Заводской Класс[^]


Patrick Skelton

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

Рейтинг:
1

F-ES Sitecore

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

public abstract class LogicBase {

    public virtual void SomeFunction()
    {
        Console.WriteLine("Logic Base");
    }
}

public class LogicSubclassA : LogicBase
{
    public string LogicSubclassAOnly => "LogicA";

    public override void SomeFunction()
    {
        Console.WriteLine(LogicSubclassAOnly);
    }
}
public class LogicSubclassB : LogicBase
{
    public string LogicSubclassBOnly => "LogicB";

    public override void SomeFunction()
    {
        Console.WriteLine(LogicSubclassBOnly);
    }
}

public static class LogicObjectFactory
{
    public static LogicBase CreateInstance(string specificType)
    {
        switch (specificType)
        {
            case "A": return new LogicSubclassA();
            case "B": return new LogicSubclassB();
            default: throw new ArgumentException();
        }
    }
}


код вызова;

var o = LogicObjectFactory.CreateInstance("A");
o.SomeFunction();

o = LogicObjectFactory.CreateInstance("B");
o.SomeFunction();


BillWoodruff

+5 работает на меня !

Patrick Skelton

Thanks for the reply. I really do seem to have done a poor job of asking this question. I put the concrete functions in just to 'test' that my two subclasses are capable of being different; in reality, the only place the code relies on its specific type is inside that specific type. The caller generally doesn't need to know the specific type. Mainly this whole thing was an exercise in avoiding casting. That said, the fact that all of the answers don't seem to line-up with my intent implies I have indeed been over-complicating things. I'll work out something a bit cleaner on Monday and post it if it looks remotely relevant to my original question. Not sure what to do about accepting solutions here. All supply useful information but it my question is poor so none of the solutions seems to be exactly what I am looking for. Any advice on protocol here anyone?

F-ES Sitecore

Основная проблема с тем, как вы это делаете, заключается в том, что вы пытаетесь привести X<A> К X<B>, и вам будет трудно сделать это легко.

Patrick Skelton

Это очень ясный способ выразить это! Является ли это все еще проблемой, если A и B сами являются подклассами общего предка Z, и все, что мне нужно сделать, это сохранить одну ссылку где-то на X<Z>, В то время как любой код, специфичный для X<A> И X<B>, содержится в частном порядке внутри этих классов?