Factory Design Pattern — зачем нужен интерфейс?

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

Официальное определение:

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

Таким образом, интерфейс кажется важной частью шаблона проектирования Factory, но единственная причина, по которой я нашел его практичным, - это когда вы создаете коллекцию в основном методе. Если вы этого не хотите, вы можете просто удалить его (посмотрите на код ниже, где это возможно), и он по-прежнему будет работать, как и планировалось.

using System;
using System.Collections.Generic;
using System.Collections;

namespace FactoryDesignPattern
{
    class Program
    {
        static void Main(string[] args)
        {
            var FordFiestaFactory = new FordFiestaFactory();
            var FordFiesta = FordFiestaFactory.CreateCar("Blue");
            Console.WriteLine("Brand: {0} \nModel: {1} \nColor: {2}", FordFiesta.Make, FordFiesta.Model, FordFiesta.Color);
            Console.WriteLine();


            //Inserted this later. Using a collection requires the Interface to be there.
            List<ICreateCars> Cars = new List<ICreateCars>();
            Cars.Add(new FordFiestaFactory());
            Cars.Add(new BMWX5Factory());

            foreach (var Car in Cars)
            {
                var ProductCar = Car.CreateCar("Red");
                Console.WriteLine("Brand: {0} \nModel: {1} \nColor: {2}", ProductCar.Make, ProductCar.Model, ProductCar.Color);
                Console.WriteLine();
            }

            Console.ReadKey();
        }
    }

    public abstract class Car
    {
        public string Make { get; set; }
        public string Model { get; set; }
        public string EngineSize { get; set; }
        public string Color { get; set; }
    }

    public class FordFiesta : Car
    {
        public FordFiesta()
        {
            Make = "Ford";
            Model = "Fiesta";
            EngineSize = "1.1";
        }
    }

    public class BMWX5 : Car
    {
        public BMWX5()
        {
            Make = "BMW";
            Model = "X5";
            EngineSize = "2.1";
        }
    }

    public interface ICreateCars
    {
        Car CreateCar(string color);
    }

    class FordFiestaFactory : ICreateCars
    {
        public Car CreateCar(string color)
        {
            return new FordFiesta() { Color = color };
        }
    }

    class BMWX5Factory : ICreateCars
    {
        public Car CreateCar(string color)
        {
            return new BMWX5(){ Color = color };
        }
    }
}

Так зачем мне этот интерфейс? Я прочитал несколько абстрактных объяснений, но не понял, поэтому предпочитаю практические ответы.

Заранее спасибо!


person Hindrik    schedule 22.12.2013    source источник
comment
Интерфейс в этом контексте обычно имеет буквальное значение интерфейса, а не интерфейса в смысле .NET.   -  person Simon Whitehead    schedule 22.12.2013


Ответы (6)


Интерфейс (или базовый класс абстрактной фабрики, который фактически аналогичен интерфейсу) полезен, когда вызывающая сторона фабрики не знает тип фабрики.

Вы предоставили основу для своего собственного практического примера, поэтому я добавлю здесь свое объяснение, почему это полезно не только при наличии списка фабрик:

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

public Car CreateCarForPerson(Person person, ICreateCars carType)
{
    if (person.OwnsCar) {
        return carType.CreateCar("red");
    } else {
        return null;
    }
}

Точно так же вы можете использовать такую ​​фабрику для создания произвольного количества автомобилей:

public Car[] CreateAnyNumberOfCars(ICreateCars carType)
{
    var result = new List<Car>();
    for (int i = new Random().Next(100); i >= 0; i--) {
        result.Add(carType.CreateCar("blue"));
    }
    return result.ToArray();
}

Обратите внимание, что ни один из этих двух методов не знает, какой тип автомобиля создается; они используют фабрику, о которой знают только интерфейс, но не точный тип.

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

person O. R. Mapper    schedule 22.12.2013

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

Вы используете фабричный шаблон, потому что не хотите, чтобы вызывающий код (основная функция) что-либо знал о деталях конструкции. Вы не хотите знать, что такое FordFiest. Вы просто хотите, чтобы автомобиль в целом был построен. Если это Форд Фиеста, то пусть будет так. Эта деталь должна обрабатываться фабрикой (и ее подклассами). Верно?

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

Надеюсь, я помог!

person Pantelis Natsiavas    schedule 22.12.2013

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

Ключ в том, чтобы решить, какой тип параметра будет у функций, которые принимают Car Factory. (или каким будет тип элемента для элемента, который хранит CarFactory) Если вы используете ICreateCars, то любой, кто хочет создавать автомобили, должен только реализовать интерфейс. Если ваш тип параметра CarFactory, то они должны наследоваться от CarFactory.

person BlueMonkMN    schedule 22.12.2013

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

Просто использование интерфейсов, как правило, является очень хорошей идеей, и в большинстве хороших компонентных проектов это широко используется. Он называется программирование на основе интерфейса. Среди его основных преимуществ — возможность использования контейнеров IoC и это делает ваш код тестируемым.

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

person Thomas Weller    schedule 22.12.2013

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

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

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

По сути, интерфейсы позволяют вам наследовать определения методов без наследования какой-либо реализации.

person Caitliin Bestler    schedule 18.02.2019

Целью Factory Pattern является абстрагирование создания. В этом случае будет возможность обменять Фабрику с соответствующей реализацией.

Абстракция может быть реализована интерфейсом или абстрактным классом в .NET.

public interface ICreateCars
{
    Car CreateCar(string color);
}

Результат построения (Car) может быть классом или интерфейсом, просто посмотрите на ситуацию. Как его построить - это задание Фабрики.

person Artru    schedule 22.12.2013