Раьаатата. Использование порождающих шаблонов
Скачать 90.2 Kb.
|
ПРАКТИЧЕСКАЯ РАБОТА № 15 Тема: Использование порождающих шаблонов Цель работы: изучение порождающих шаблонов и умение их использовать при написании программ Оборудование: ПК, программное обеспечение – Visual Studio, инструкции по выполнению работы. Справочный материал: Паттерн Builder принадлежит к порождающим паттернам и используется для порождения объектов. Необходимость использования паттерна Builder в программе возникает в случаях, когда нужно добавлять новые возможности без существенного изменения кода. Под возможностями понимаются дополнительные преобразования, которые генерируют конечный продукт (объект). В паттерне Builder представление объекта отделяется от его конструирования (построения). При этом, для конкретной конструкции получаются разные представления. Паттерн Singleton (одиночка, одиночка) относится к порождающим паттернам. Паттерн Singleton предназначен для создания заданного количества экземпляров (объектов) класса. Чаще всего паттерн Singleton используется для создания гарантированно одного экземпляра класса. Паттерн Singleton важен в случаях, когда для некоторых классов нужно чтобы существовало определенное количество экземпляров, например один экземпляр. Содержание работы: Задание 1. Реализация паттерна Builder на языке C#, структура которого изображена на рисунке Объявляются следующие классы: Product — класс, который является продуктом (результатом работы паттерна). Это произвольный класс. В демонстрационных целях в этом классе объявляется три целочисленных переменных, каждая из которых условно считается частью продукта; Builder — абстрактный класс, который служит интерфейсом между распорядителем (Director) и классом, создающим конкретный объект (ConcreteBuilder); ConcreteBuilder — класс, который строит конкретный объект; Director — класс, являющийся распорядителем. Этот класс строит (конфигурирует) объект класса ConcreteBuilder для использования его клиентом. Program — класс, который выступает в роли клиента. В этом классе реализуется функция main(), в которой продемонстрирована работа клиента. Клиент обращается к распорядителю (Director) для того, чтобы тот построил объект класса ConcreteBuilder. Затем построенный объект возвращается клиенту в методе GetResult(). Текст программы: using System; namespace ConsoleApp8 { // Паттерн Builder. Пример реализации на C# // 1. Класс, служащий продуктом. Содержит три части class Product { // Внутренние переменные private int part1, part2, part3; // Конструктор public Product() { part1 = 0; part2 = 0; part3 = 0; } // Методы доступа public int GetPart1() { return part1; } public int GetPart2() { return part2; } public int GetPart3() { return part3; } public void SetPart1(int part1) { this.part1 = part1; } public void SetPart2(int part2) { this.part2 = part2; } public void SetPart3(int part3) { this.part3 = part3; } } // 2. Класс, который служит интерфейсом между распорядителем и конкретным строителем abstract class Builder { // Метод, создающий продукт public abstract void CreateProduct(); // Методы, которые строят части продукта public abstract void BuildPart1(int part); public abstract void BuildPart2(int part); public abstract void BuildPart3(int part); // Метод, возвращающий продукт клиенту public abstract Product GetProduct(); } // Класс - конкретный строитель, наследует абстрактный класс Builder class ConcreteBuilder: Builder { // Внутренняя переменная - ссылка на продукт private Product currentBuilder; // Методы, которые объявляются в абстрактном классе Builder, их нужно реализовывать public override void CreateProduct() { currentBuilder = new Product(); } public override void BuildPart1(int part) { currentBuilder.SetPart1(part); } public override void BuildPart2(int part) { currentBuilder.SetPart2(part); } public override void BuildPart3(int part) { currentBuilder.SetPart3(part); } public override Product GetProduct() { return currentBuilder; } } // Класс-распорядитель, содержит методы построения объекта Product из частей class Director { // Ссылка на Builder private Builder builder; // Конструктор - инициализируется экземпляром Builder public Director(Builder _builder) { builder = _builder; } // Метод, который строит объект класса Product из частей public void Construct() { // Построить экземпляр класса Product builder.CreateProduct(); builder.BuildPart1(10); builder.BuildPart2(20); builder.BuildPart3(30); } } // Класс, содержащий функцию клиента class Program { static void Main(string[] args) { // Это клиент // 1. Объявить ссылку на продукт Product product; // 2. Создать конкретного строителя Builder B = new ConcreteBuilder(); // 3. Создать распорядителя и сконфигурировать его строителем Director D = new Director(B); // 4. Вызвать методы построения продукта - доверить это распорядителю D.Construct(); // 5. Вернуть построенный продукт клиенту product = B.GetProduct(); // 6. Проверить, как построен продукт Console.WriteLine("product.part1 = {0}", product.GetPart1()); Console.WriteLine("product.part2 = {0}", product.GetPart2()); Console.WriteLine("product.part3 = {0}", product.GetPart3()); } } } Результат работы программы – прислать скриншот Задание 2. Написать программу на C#, реализующую паттерн Singletonusing System; using static System.Console; namespace ConsoleApp9{ // Реализация паттерна Singleton - Одиночка class Singleton { // Статический метод, который возвращает экземпляр класса Singleton. // Данный метод может быть заменен соответствующим свойством. public static Singleton Instance() { if (_instance == null) { _instance = new Singleton(); return _instance; } else { return null; } } // Конструктор класса, объявленный как protected, для того чтобы: // - запретить создание экземпляра класса оператором new; // - можно было наследовать данный класс. protected Singleton() { } // Статическая внутренняя переменная, которая хранит экземпляр класса. // К этой переменной есть доступ из методов данного класса. // Из методов других классов доступа к переменной нет. private static Singleton _instance = null; // ------------------------------------------------- // Другие внутренние поля класса private int d; // Свойство для доступа к полю d public int D { get { return d; } set { d = value; } } // Метод, который выводит значение поля d public void Print(string text) { WriteLine("------------------"); WriteLine("{0}. d = {1}", text, d); } } // Класс, выступающий клиентом class Program { static void Main(string[] args) { // Это есть код клиента. // Создать единственный экземпляр класса Singleton Singleton obj1 = Singleton.Instance(); // Проверить obj1 на равенство null if (obj1 != null) { obj1.D = 25; obj1.Print("obj1"); } else WriteLine("obj1 == null"); // Попытка создать другой экземпляр класса Singleton obj2 = Singleton.Instance(); if (obj2 != null) { obj2.D = 77; obj2.Print("obj2"); } else WriteLine("obj2 == null"); } } } Результат выполнения программы: прислать скриншот Задание 3. Написать программу на C#, реализующую паттерн AbstractFactory using System; namespace RefactoringGuru.DesignPatterns.AbstractFactory.Conceptual { // Интерфейс Абстрактной Фабрики объявляет набор методов, которые возвращают различные абстрактные продукты. Эти продукты называются семейством и связаны темой или концепцией высокого уровня. Продукты одного семейства обычно могут взаимодействовать между собой. Семейство продуктов может // иметь несколько вариаций, но продукты одной вариации несовместимы с продуктами другой. public interface IAbstractFactory { IAbstractProductA CreateProductA(); IAbstractProductB CreateProductB(); } // Конкретная Фабрика производит семейство продуктов одной вариации. Фабрика гарантирует совместимость полученных продуктов. Обратите внимание, что сигнатуры методов Конкретной Фабрики возвращают абстрактный продукт, в то время как внутри метода создается экземпляр конкретного продукта. class ConcreteFactory1 : IAbstractFactory { public IAbstractProductA CreateProductA() { return new ConcreteProductA1(); } public IAbstractProductB CreateProductB() { return new ConcreteProductB1(); } } // Каждая Конкретная Фабрика имеет соответствующую вариацию продукта. class ConcreteFactory2 : IAbstractFactory { public IAbstractProductA CreateProductA() { return new ConcreteProductA2(); } public IAbstractProductB CreateProductB() { return new ConcreteProductB2(); } } // Каждый отдельный продукт семейства продуктов должен иметь базовый интерфейс. Все вариации продукта должны реализовывать этот интерфейс. public interface IAbstractProductA { string UsefulFunctionA(); } // Конкретные продукты создаются соответствующими Конкретными Фабриками. class ConcreteProductA1 : IAbstractProductA { public string UsefulFunctionA() { return "The result of the product A1."; } } class ConcreteProductA2 : IAbstractProductA { public string UsefulFunctionA() { return "The result of the product A2."; } } // Базовый интерфейс другого продукта. Все продукты могут взаимодействовать друг с другом, но правильное взаимодействие возможно только между продуктами одной и той же конкретной вариации. public interface IAbstractProductB { // Продукт B способен работать самостоятельно... string UsefulFunctionB(); // ...а также взаимодействовать с Продуктами А той же вариации. Абстрактная Фабрика гарантирует, что все продукты, которые она создает, имеют одинаковую вариацию и, следовательно, совместимы. string AnotherUsefulFunctionB(IAbstractProductA collaborator); } // Конкретные Продукты создаются соответствующими Конкретными Фабриками. class ConcreteProductB1 : IAbstractProductB { public string UsefulFunctionB() { return "The result of the product B1."; } // Продукт B1 может корректно работать только с Продуктом A1. Тем не менее, он принимает любой экземпляр Абстрактного Продукта А в качестве аргумента. public string AnotherUsefulFunctionB(IAbstractProductA collaborator) { var result = collaborator.UsefulFunctionA(); return $"The result of the B1 collaborating with the ({result})"; } } class ConcreteProductB2 : IAbstractProductB { public string UsefulFunctionB() { return "The result of the product B2."; } // Продукт B2 может корректно работать только с Продуктом A2. Тем не менее, он принимает любой экземпляр Абстрактного Продукта А в качестве аргумента. public string AnotherUsefulFunctionB(IAbstractProductA collaborator) { var result = collaborator.UsefulFunctionA(); return $"The result of the B2 collaborating with the ({result})"; } } // Клиентский код работает с фабриками и продуктами только через абстрактные типы: Абстрактная Фабрика и Абстрактный Продукт. Это позволяет передавать любой подкласс фабрики или продукта клиентскому коду, не нарушая его. class Client { public void Main() { // Клиентский код может работать с любым конкретным классом фабрики. Console.WriteLine("Client: Testing client code with the first factory type..."); ClientMethod(new ConcreteFactory1()); Console.WriteLine(); Console.WriteLine("Client: Testing the same client code with the second factory type..."); ClientMethod(new ConcreteFactory2()); } public void ClientMethod(IAbstractFactory factory) { var productA = factory.CreateProductA(); var productB = factory.CreateProductB(); Console.WriteLine(productB.UsefulFunctionB()); Console.WriteLine(productB.AnotherUsefulFunctionB(productA)); } } class Program { static void Main(string[] args) { new Client().Main(); } } } Результат выполнения программы: прислать скриншот Задание 4. Написать программу на C#, реализующую паттерн FactoryMethod using System; namespace RefactoringGuru.DesignPatterns.FactoryMethod.Conceptual{ // Класс Создатель объявляет фабричный метод, который должен возвращать объект класса Продукт. Подклассы Создателя обычно предоставляют реализацию этого метода. abstract class Creator { // Обратите внимание, что Создатель может также обеспечить реализацию фабричного метода по умолчанию. public abstract IProduct FactoryMethod(); // Также заметьте, что, несмотря на название, основная обязанность Создателя не заключается в создании продуктов. Обычно он содержит некоторую базовую бизнес-логику, которая основана на объектах Продуктов, возвращаемых фабричным методом. Подклассы могут косвенно изменять эту бизнес-логику, переопределяя фабричный метод и возвращая из него другой тип продукта. public string SomeOperation() { // Вызываем фабричный метод, чтобы получить объект-продукт. var product = FactoryMethod(); // Далее, работаем с этим продуктом. var result = "Creator: The same creator's code has just worked with " + product.Operation(); return result; } } // Конкретные Создатели переопределяют фабричный метод для того, чтобы // изменить тип результирующего продукта. class ConcreteCreator1 : Creator { // Обратите внимание, что сигнатура метода по-прежнему использует тип абстрактного продукта, хотя фактически из метода возвращается конкретный продукт. Таким образом, Создатель может оставаться независимым от конкретных классов продуктов. public override IProduct FactoryMethod() { return new ConcreteProduct1(); } } class ConcreteCreator2 : Creator { public override IProduct FactoryMethod() { return new ConcreteProduct2(); } } // Интерфейс Продукта объявляет операции, которые должны выполнять все конкретные продукты. public interface IProduct { string Operation(); } // Конкретные Продукты предоставляют различные реализации интерфейса Продукта. class ConcreteProduct1 : IProduct { public string Operation() { return "{Result of ConcreteProduct1}"; } } class ConcreteProduct2 : IProduct { public string Operation() { return "{Result of ConcreteProduct2}"; } } class Client { public void Main() { Console.WriteLine("App: Launched with the ConcreteCreator1."); ClientCode(new ConcreteCreator1()); Console.WriteLine(""); Console.WriteLine("App: Launched with the ConcreteCreator2."); ClientCode(new ConcreteCreator2()); } // Клиентский код работает с экземпляром конкретного создателя, хотя и через его базовый интерфейс. Пока клиент продолжает работать с создателем через базовый интерфейс, вы можете передать ему любой подкласс создателя. public void ClientCode(Creator creator) { Console.WriteLine("Client: I'm not aware of the creator's class," + "but it still works.\n" + creator.SomeOperation()); } } class Program { static void Main(string[] args) { new Client().Main(); } } } Результат выполнения программы: прислать скриншот Задание 5. Написать программу на C#, реализующую паттерн Prototype using System; namespace RefactoringGuru.DesignPatterns.Prototype.Conceptual{ public class Person { public int Age; public DateTime BirthDate; public string Name; public IdInfo IdInfo; public Person ShallowCopy() { return (Person) this.MemberwiseClone(); } public Person DeepCopy() { Person clone = (Person) this.MemberwiseClone(); clone.IdInfo = new IdInfo(IdInfo.IdNumber); clone.Name = String.Copy(Name); return clone; } } public class IdInfo { public int IdNumber; public IdInfo(int idNumber) { this.IdNumber = idNumber; } } class Program { static void Main(string[] args) { Person p1 = new Person(); p1.Age = 21; p1.BirthDate = Convert.ToDateTime("2000-01-01"); p1.Name = "Иван Петров"; p1.IdInfo = new IdInfo(36); // Выполнить поверхностное копирование p1 и присвоить её p2. Person p2 = p1.ShallowCopy(); // Сделать глубокую копию p1 и присвоить её p3. Person p3 = p1.DeepCopy(); // Вывести значения p1, p2 и p3. Console.WriteLine("Original values of p1, p2, p3:"); Console.WriteLine(" p1 instance values: "); DisplayValues(p1); Console.WriteLine(" p2 instance values:"); DisplayValues(p2); Console.WriteLine(" p3 instance values:"); DisplayValues(p3); // Изменить значение свойств p1 и отобразить значения p1, p2 и p3. p1.Age = 30; p1.BirthDate = Convert.ToDateTime("1991-11-09"); p1.Name = "Илья Сергеев"; p1.IdInfo.IdNumber = 78; Console.WriteLine("\nValues of p1, p2 and p3 after changes to p1:"); Console.WriteLine(" p1 instance values: "); DisplayValues(p1); Console.WriteLine(" p2 instance values (reference values have changed):"); DisplayValues(p2); Console.WriteLine(" p3 instance values (everything was kept the same):"); DisplayValues(p3); } public static void DisplayValues(Person p) { Console.WriteLine(" Name: {0:s}, Age: {1:d}, BirthDate: {2:MM/dd/yy}", p.Name, p.Age, p.BirthDate); Console.WriteLine(" ID#: {0:d}", p.IdInfo.IdNumber); } }} Результат выполнения программы: прислать скриншот Задание 6. Используя средства языка C# и возможности паттерна Builder разработать программу для генерирования объектов, являющихся массивами случайных чисел. Массивы случайных чисел представлены следующими классами: ArrayRandomInt — массив из целых чисел. Для данного массива указывается диапазон; ArrayRandomChar — массив случайных символов от ‘A’ до ‘Z’. Задание 7. Используя средства языка C# и паттерна FactoryMethod разработать программу для работы менеджера по найму. Невозможно одному человеку провести собеседования со всеми кандидатами на все вакансии. В зависимости от вакансии он должен распределить этапы собеседования между разными людьми. |