Описание
Даниил Дмитриевич## На чем это все написано?
C# + Avalonia + MVVM Pattern
---
## Про сам паттерн
#### Объяснение для преподавателя:
Паттерн "Абстрактная фабрика" (Abstract Factory) относится к паттернам создания (Creational Patterns) и предоставляет интерфейс для создания семейств взаимосвязанных объектов, не указывая их конкретных классов.
Основные составляющие паттерна "Абстрактная фабрика":
1. Абстрактная фабрика (Abstract Factory): определяет интерфейс для создания семейств связанных объектов. Обычно содержит абстрактные методы для создания каждого объекта в семействе.
2. Конкретная фабрика (Concrete Factory): реализует абстрактную фабрику и создает конкретные объекты семейства.
3. Абстрактный продукт (Abstract Product): определяет интерфейс для продукта, который будет создан фабрикой.
4. Конкретный продукт (Concrete Product): реализует абстрактный продукт и представляет конкретный объект, который будет создан фабрикой.
5. Клиент (Client): использует абстрактную фабрику и продукты, созданные ею. Клиент не зависит от конкретных классов продуктов, он работает только с абстрактными интерфейсами.
Паттерн "Абстрактная фабрика" позволяет создавать семейства объектов, которые взаимодействуют друг с другом и должны быть созданы вместе. Он обеспечивает гибкость и изолирует клиентский код от конкретных классов продуктов, позволяя легко заменять семейства продуктов без изменения клиентского кода.
---
#### Нормальное объяснение:
Как уже было написано, данный паттерн является порождающим, а значит служит для создания новых объектов. Зачем это надо и почему нельзя ручками создать самому объект? Допустим, у нас есть большой класс с кучей полей и свойств, соответственно будет много параметров в конструкторе. Это нам не подходит и каждый раз создавать новый объект такого класса довольно проблемно.
Собственно решение: нам нужно инкапсулировать процесс создания нового объекта. Не совсем понятно, да? Короче, нам нужно скрыть от глаз создания объекта. Мы просто должны сказать: "Хочу вот это" и все, программа должна сделать объект сама по заданным ранее условиям.
Что для этого надо?
Нам нужна одна абстрактная фабрика, в которой мы зададим методы для создания объектов. Далее в настоящих (не абстрактных, а реальных фабриках) мы прописываем создания обьекта, то есть заполняем в самом простом случае большой конструктор.
Так же у нас должен быть базовый обьект (продукт) для создания и от него отходят реальные продукты, которые, разумеется, следуя логике наследования, могут содержать разные поля.
Рассмотрим на нашем примере.
У нас есть базовый или же абстрактный предмет - Electronics.
```csharp
public abstract class Electronics
{
private double _price;
private int _powerConsumprion;
public string Brand { get; set; }
public string Model { get; set; }
public double Price
{
get
{
return _price;
}
set
{
if (value <= 0)
{
throw new ArgumentException();
}
_price = value;
}
}
public int PowerConsumprion
{
get
{
return _powerConsumprion;
}
set
{
if (value <= 0)
{
throw new ArgumentException();
}
_powerConsumprion = value;
}
}
public Electronics(string brand, string model, double price, int powerConsumption)
{
Brand = brand;
Model = model;
Price = price;
PowerConsumprion = powerConsumption;
}
}
```
В нем мы определили базовые свойства, а уже потом от этого класса наследуем другие, такие как `Laptop`, `Smartphone`, `TV`. В каждом классе есть что-то свое характерное.
Далее мы создаем абстрактную фабрику.
```csharp
public abstract class ElectonicsFactory
{
public abstract Electronics.Electronics CreateLaptop();
public abstract Electronics.Electronics CreateSmartphone();
public abstract Electronics.Electronics CreateTV();
}
```
В этой фабрике есть методы для создания товаров. Наследуясь от этой фабрики мы обязуемся реализовать все методы. То есть, каждая фабрика по-своему делает товары, в этом и заключается логика паттерна. Мы абстрагируемся от процесса создания товара, а лишь получаем готовый. Нам не важно, как оно все создавалось и как работает внутри, нам важен и нужен лишь итоговый продукт, а как он будет сделан - за это отвечает конкретная фабрика.
Вот так делает наши товары фабрика Apple:
```csharp
public class AppleFactory : ElectonicsFactory
{
public override Electronics.Electronics CreateLaptop()
{
return new Laptop("Apple",
"Macbook",
1000,
85,
"M1",
13.5,
DisplayType.IPS,
512);
}
public override Electronics.Electronics CreateSmartphone()
{
return new Smartphone("Apple",
"iPhone",
800,
15,
7.5,
12,
DisplayType.Oled,
OperationSystemType.IOS);
}
public override Electronics.Electronics CreateTV()
{
return new TV("Apple",
"HomeTV",
1200,
120,
DisplayType.IPS,
55,
true);
}
}
```
А вот так, уже иначе, делает это Samsung:
```csharp
public class SamsungFactory : ElectonicsFactory
{
public override Electronics.Electronics CreateLaptop()
{
return new Laptop("Samsung",
"Galaxy Book",
750,
85,
"Intel Core i7",
14.5,
DisplayType.IPS,
256);
}
public override Electronics.Electronics CreateSmartphone()
{
return new Smartphone("Samsung",
"Galaxy 7",
470,
15,
7.5,
16,
DisplayType.Oled,
OperationSystemType.Android);
}
public override Electronics.Electronics CreateTV()
{
return new TV("Samsung",
"SmartTV",
1200,
120,
DisplayType.IPS,
55,
true);
}
}
```
Но нам в любом случае это не интересно, мы просто закажем у одной из этой фабрик телефон или ноутбук и все.
Другой пример из жизни.
В играх часто есть однотипные мобы. Допустим, это будут зомби. Неужели разработчик каждый раз прописывает для одинаковых зомби один и тот же процесс создания? Нет, однозначно нет. Что сделает грамотный разработчик? Он создаст фабрику, которая будет за него делать этих несчастных зомби. А что если он захочет еще и делать злых крыс? Добавим процесс создания крыс в нашу фабрику. Теперь не надо париться над созданием, просто: "Эй, фабрика, сделай мне парочку зомби и 5 крыс".
Ага, круто, но что если мы захотим делать более злых, страшных зомби и крыс? Надо создать новую фабрику и в ней по-другому прописать создание таких объектов, вот и все дела. Новый уровень - новая фабрика по созданию мобов. Так же можно использовать несколько фабрик.
---
## Структура проекта
- `Assets` - ресурсы проекта (изображения)
- `Models` - модели (данные)
- `Electronics` - модели электроники (товары)
- `Electronics` - абстрактный класс продукта (базовый для дальнейшего наследования)
- `Laptop`
- `Smartphone`
- `TV`
- `Factories`
- `ElectronicsFactory` - абстрактный класс фабрики (базовый для дальнейшего наследования)
- `AppleFactory`
- `SamsungFactory`
- `SonyFactory`
- `Interfaces`
- `IHaveOperaionSystem` - интерфейс для тех, у кого есть операционная систем
- `IHaveScreen` - интерфейс для тех, у кого есть экран
- `Types`
- `DisplayType` - типы экранов
- `OperationSystemType` - типы операционных систем
- `ViewModels` - клей между данными и отображением
- `ElectonicsViewModel` - данные для отображения типа продукта (телефон, ТВ, ноутбук)
- `FactoryViewModel` - данные для отображения типа производителя
- `MainViewModel` - главное окно, в ней реализована логика перехода по страницам, логика создания при выборе
- `ModelPageViewModel` - данные для отображения созданного продукта
- `SelectElectronicsPageViewModel` - данные для отображения выбора продукта
- `SelectFactoryPageViewModel` - данные для отображения выбора производителя
- `ViewModelBase`
- `Views` - отображение
- `MainWindow`
- `ModelPageView`
- `SelectElectronicsPageView`
- `SelectFactoryPageView`
## Замечания от автора
- НЕ стоит париться над графической частью, лучше вникнуть в саму логику, рассмотреть модели программы и как они между собой взаимодействуют
- Интерфейсы и типы были лишь созданы для демонстрации навыков программирования для преподавателя
- Лучший способ отблагодарить программиста (помимо чаевых, разумеется) - сделать новый заказ и оставить хороший отзыв. Спасибо!