Порождающие паттерны
Factory Method, Abstract Factory
OOP1Creat1°. 
Factory Method (Фабричный метод) — порождающий паттерн. Известен также под именем Virtual Constructor (Виртуальный конструктор). Частота использования: высокая. Назначение: определяет интерфейс для создания объекта, но оставляет подклассам решение о том, какой класс инстанцировать. Фабричный метод позволяет классу делегировать инстанцирование подклассам. Участники: • Product (Продукт) — определяет интерфейс объектов, создаваемых фабричным методом; • ConcreteProduct (Конкретный продукт) — реализует интерфейс Product; • Creator (Создатель) — объявляет фабричный метод, возвращающий объект типа Product; может также определять реализацию фабричного метода по умолчанию, возвращающую некоторый конкретный продукт; реализует методы, в которых используется объект Product, созданный фабричным методом; • ConcreteCreator (Конкретный создатель) — замещает фабричный метод для создания конкретного продукта. Задание 1. Реализовать две иерархии классов, в одну из которых входят абстрактный создатель Creator и два конкретных создателя ConcreteCreator1 и ConcreteCreator2, а в другую — абстрактный продукт Product и два конкретных продукта ConcreteProduct1 и ConcreteProduct2. Абстрактный класс Product содержит два абстрактных метода, связанных с получением и преобразованием строки: метод GetInfo без параметров, возвращающий строку, и метод Transform без параметров, который ничего не возвращает. Классы ConcreteProduct1 и ConcreteProduct2 содержат строковое поле info, которое инициализируется в конструкторе с помощью одноименного параметра, после чего в конструкторе класса ConcreteProduct1 поле info преобразуется к нижнему регистру, а в конструкторе класса ConcreteProduct2 — к верхнему. Метод GetInfo в каждом подклассе возвращает текущее значение поля info, а метод Transform преобразует это поле следующим образом: для ConcreteProduct1 он добавляет дополнительный пробел после каждого непробельного символа поля info (кроме его последнего символа), а для ConcreteProduct2 он добавляет два дополнительных символа * (звездочка) после каждого символа, отличного от звездочки (кроме последнего символа). Абстрактный класс Creator содержит абстрактный фабричный метод FactoryMethod(info) со строковым параметром info, возвращающий ссылку на объект Product. Этот метод определяется в классах ConcreteCreator1 и ConcreteCreator2, причем фабричный метод класса ConcreteCreator1 создает объект типа ConcreteProduct1, а фабричный метод класса ConcreteCreator2 создает объект типа ConcreteProduct2; в любом случае конструктору создаваемого объекта передается параметр info фабричного метода. В абстрактном классе Creator дополнительно определить метод AnOperation(info), который создает продукт с помощью фабричного метода, передавая ему параметр info, дважды вызывает метод Transform созданного продукта и с помощью его метода GetInfo возвращает полученный результат. Использование фабричного метода в методе AnOperation приводит к тому, что выполнение метода AnOperation в подклассах класса Creator дает различные результаты, зависящие от свойств создаваемых продуктов, причем такое поведение реализуется без изменения кода метода AnOperation. Тестирование разработанной системы классов. Даны пять строк. Используя конкретных создателей 1 и 2, применить к каждой из данных строк метод AnOperation и вывести возвращаемый результат этого метода (вначале выводятся результаты для первой строки, затем для второй и т. д.).
[C++] class Product
{
public:
virtual string GetInfo() = 0;
virtual void Transform() = 0;
virtual ~Product()
{
Show("Product");
}
};
// Implement the ConcreteProduct1
// and ConcreteProduct2 descendant classes
class Creator
{
protected:
virtual shared_ptr<Product> FactoryMethod(string info) = 0;
public:
string AnOperation(string info);
virtual ~Creator()
{
Show("Creator");
}
};
string Creator::AnOperation(string info)
{
auto p = FactoryMethod(info);
p->Transform();
p->Transform();
return p->GetInfo();
}
// Implement the ConcreteCreator1
// and ConcreteCreator2 descendant classes;
// the AnOperation method should not be
// overridden in these classes
[C#] public abstract class Product
{
public abstract string GetInfo();
public abstract void Transform();
}
// Implement the ConcreteProduct1
// and ConcreteProduct2 descendant classes
public abstract class Creator
{
protected abstract Product FactoryMethod(string info);
public string AnOperation(string info)
{
Product p = FactoryMethod(info);
p.Transform();
p.Transform();
return p.GetInfo();
}
}
// Implement the ConcreteCreator1
// and ConcreteCreator2 descendant classes;
// the AnOperation method should not be
// overridden in these classes
[Java] abstract class Product {
public abstract String getInfo();
public abstract void transform();
}
// Implement the ConcreteProduct1
// and ConcreteProduct2 descendant classes
abstract class Creator {
protected abstract Product factoryMethod(String info);
public String anOperation(String info) {
Product p = factoryMethod(info);
p.transform();
p.transform();
String s = p.getInfo();
return s;
}
}
// Implement the ConcreteCreator1
// and ConcreteCreator2 descendant classes;
// the anOperation method should not be
// overridden in these classes
[Python] class ConcreteProduct1:
def __init__(self, info):
pass
# Implement the "constructor"
def getInfo(self):
pass
# Implement the method
def transform(self):
pass
# Implement the method
# Implement the ConcreteProduct2 class
class Creator:
def anOperation(self, info):
p = self.factoryMethod(info)
p.transform()
p.transform()
return p.getInfo()
class ConcreteCreator1(Creator):
def factoryMethod(self, info):
pass
# Implement the method
# Implement the ConcreteCreator2 descendant class;
# the anOperation method should not be
# overridden in this class
[Ruby] class ConcreteProduct1
def initialize(info)
# Implement the "constructor"
end
def getInfo
# Implement the method
end
def transform
# Implement the method
end
end
# Implement the ConcreteProduct2 class
class Creator
def anOperation(info)
p = factoryMethod(info)
p.transform
p.transform
p.getInfo
end
end
class ConcreteCreator1 < Creator
def factoryMethod(info)
# Implement the method
end
end
# Implement the ConcreteCreator2 descendant class;
# the anOperation method should not be
# overridden in this class
Указание (Julia). При определении конструкторов ConcreteProduct1 и ConcreteProduct2 следует использовать внутренний конструктор new , чтобы избежать бесконечной рекурсии.
[Julia] abstract type Product
end
abstract type Creator
end
mutable struct ConcreteProduct1 <: Product
info::String
ConcreteProduct1(info::String) = new(lowercase(info))
end
function getInfo(p::ConcreteProduct1)
p.info
end
function transform!(p::ConcreteProduct1)
# Implement the function
end
struct ConcreteCreator1 <: Creator
end
function factoryMethod(::ConcreteCreator1, info::String)
ConcreteProduct1(info)
end
# Implement the ConcreteProduct2,
# ConcreteCreator2 descendant structs
# and all required functions;
# the anOperation function should not be
# overloaded for the ConcreteCreator1
# and ConcreteCreator2 structs
function anOperation(c::Creator, info::String)
p = factoryMethod(c, info)
transform!(p)
transform!(p)
getInfo(p)
end
[PascalABC.NET] type
Product = abstract class
public
function GetInfo: string; abstract;
procedure Transform; abstract;
end;
// Реализуйте классы-потомки ConcreteProduct1 и ConcreteProduct2
Creator = abstract class
protected
function FactoryMethod(info: string): Product; abstract;
public
function AnOperation(info: string): string;
begin
var p := FactoryMethod(info);
p.Transform;
p.Transform;
Result := p.GetInfo;
end;
end;
// Реализуйте классы-потомки ConcreteCreator1 и ConcreteCreator2.
// В этих классах не следует переопределять метод AnOperation
OOP1Creat2°. Factory Method (Фабричный метод) — порождающий паттерн. Задание 2. Данное задание аналогично предыдущему (см. OOP1Creat1), однако в нем не используются абстрактные классы. Иерархию классов-продуктов представляет конкретный класс ConcreteProduct1 и его потомок ConcreteProduct2, иерархию классов-создателей представляет конкретный класс ConcreteCreator1 и его потомок ConcreteCreator2. Как и в предыдущем задании, классы-продукты обеспечивают хранение и получение строки (поле info и метод GetInfo, который возвращает значение поля info), а также ее преобразование (метод Transform). Поле info инициализируется в конструкторе с помощью одноименного параметра, после чего конструктор класса ConcreteProduct1 преобразует поле info к нижнему регистру, а конструктор класса ConcreteProduct2 выполняет то же действие, но дополнительно преобразует первый символ строки к верхнему регистру. Метод Transform для конкретного продукта 1 добавляет дополнительный символ * (звездочка) после каждого символа исходной строки info, отличного от звездочки (кроме последнего символа строки). Метод Transform для конкретного продукта 2 выполняет те же действия и дополнительно добавляет по два символа = (знак равенства) в начало и конец строки. В классах ConcreteProduct1 и ConcreteProduct2 определить фабричный метод FactoryMethod(info) со строковым параметром, возвращающий ссылку на объект ConcreteProduct1. Фабричный метод класса ConcreteCreator1 создает объект типа ConcreteProduct1, а фабричный метод класса ConcreteCreator2 создает объект типа ConcreteProduct2; в любом случае конструктору создаваемого объекта передается параметр info фабричного метода. В классе ConcreteCreator1 также определить метод AnOperation(info), который создает продукт с помощью фабричного метода, передавая ему параметр info, дважды вызывает метод Transform созданного продукта и с помощью его метода GetInfo возвращает полученный результат. Тестирование разработанной системы классов. Даны пять строк. Используя конкретных создателей 1 и 2, применить к каждой из данных строк метод AnOperation и вывести возвращаемый результат этого метода (вначале выводятся результаты для первой строки, затем для второй и т. д.).
Указание (C++). При определении конструктора класса ConcreteProduct2 используйте выражение : ConcreteProduct1(info) после его заголовка (для вызова конструктора предка), при модификации метода Transform класса ConcreteProduct2 используйте вызов ConcreteProduct1::Transform() .
[C++] class ConcreteProduct1
{
protected:
string info;
public:
string GetInfo()
{
return info;
}
ConcreteProduct1(string info);
virtual void Transform();
virtual ~ConcreteProduct1()
{
Show("ConcreteProduct1");
}
};
ConcreteProduct1::ConcreteProduct1(string info)
{
// Implement the constructor
}
void ConcreteProduct1::Transform()
{
// Implement the method
}
// Implement the ConcreteProduct2 descendant class
class ConcreteCreator1
{
protected:
virtual shared_ptr<ConcreteProduct1> FactoryMethod(string info)
{
return make_shared<ConcreteProduct1>(info);
}
public:
string AnOperation(string info);
virtual ~ConcreteCreator1()
{
Show("ConcreteCreator1");
}
};
string ConcreteCreator1::AnOperation(string info)
{
auto p = FactoryMethod(info);
p->Transform();
p->Transform();
string s = p->GetInfo();
delete p;
return s;
}
// Implement the ConcreteCreator2 descendant class
Указание (C#). При определении конструктора класса ConcreteProduct2 используйте выражение : base(info) после его заголовка (для вызова конструктора предка), при модификации метода Transform класса ConcreteProduct2 используйте вызов base.Transform() .
[C#] public class ConcreteProduct1
{
protected string info;
public string GetInfo()
{
return info;
}
public ConcreteProduct1(string info)
{
// Implement the constructor
}
public virtual void Transform()
{
// Implement the method
}
}
// Implement the ConcreteProduct2 descendant class
public class ConcreteCreator1
{
protected virtual ConcreteProduct1 FactoryMethod(string info)
{
return new ConcreteProduct1(info);
}
public string AnOperation(string info)
{
ConcreteProduct1 p = FactoryMethod(info);
p.Transform();
p.Transform();
return p.GetInfo();
}
}
// Implement the ConcreteCreator2 descendant class
Указание (Java). При определении конструктора класса ConcreteProduct2 используйте выражение : super(info) после его заголовка (для вызова конструктора предка), при модификации метода transform класса ConcreteProduct2 используйте вызов super.transform() .
[Java] class ConcreteProduct1 {
protected String info;
public ConcreteProduct1(String info) {
// Implement the constructor
}
public String getInfo() {
return info;
}
public void transform() {
// Implement the method
}
}
// Implement the ConcreteProduct2 descendant class
class ConcreteCreator1 {
protected ConcreteProduct1 factoryMethod(String info) {
return new ConcreteProduct1(info);
}
public String anOperation(String info) {
ConcreteProduct1 p = factoryMethod(info);
p.transform();
p.transform();
String s = p.getInfo();
return s;
}
}
// Implement the ConcreteCreator2 descendant class
Указание (Python). При определении конструктора класса ConcreteProduct2 используйте выражение : super().__init__(info) после его заголовка (для вызова конструктора предка), при модификации метода transform класса ConcreteProduct2 используйте вызов super().transform() .
[Python] class ConcreteProduct1:
def __init__(self, info):
pass
# Implement the "constructor"
def getInfo(self):
pass
# Implement the method
def transform(self):
pass
# Implement the method
# Implement the ConcreteProduct2 descendant class
class ConcreteCreator1:
def factoryMethod(self, info):
return ConcreteProduct1(info)
def anOperation(self, info):
p = self.factoryMethod(info)
p.transform()
p.transform()
return p.getInfo()
# Implement the ConcreteCreator2 descendant class
Указание (Ruby). При определении конструктора класса ConcreteProduct2 используйте выражение : super после его заголовка (для вызова конструктора предка), при модификации метода transform класса ConcreteProduct2 используйте вызов super .
[Ruby] class ConcreteProduct1
def initialize(info)
# Implement the "constructor"
end
def getInfo
# Implement the method
end
def transform
# Implement the method
end
end
# Implement the ConcreteProduct2 descendant class
class ConcreteCreator1
def factoryMethod(info)
ConcreteProduct1.new(info)
end
def anOperation(info)
p = factoryMethod(info)
p.transform
p.transform
p.getInfo
end
end
# Implement the ConcreteCreator2 descendant class
Указание 1 (Julia). При определении конструктора ConcreteProduct1 используйте внутренний конструктор new , чтобы избежать бесконечной рекурсии.
Указание 2 (Julia). Так как в Julia не могут наследоваться конкретные типы, при реализации структуры ConcreteProduct2 добавьте в нее поле типа ConcreteProduct1 . Для ConcreteCreator1 и ConcreteCreator2 , как и в предыдущей задаче, реализуйте наследование от абстрактного типа Creator .
[Julia] mutable struct ConcreteProduct1
info::String
# Implement the constructor
end
abstract type Creator
end
struct ConcreteCreator1 <: Creator
end
function getInfo(p::ConcreteProduct1)
p.info
end
function transform!(p::ConcreteProduct1)
# Implement the method
end
function anOperation(c::Creator, info::String)
p = factoryMethod(c, info)
transform(p)
transform(p)
getInfo(p)
end
function factoryMethod(::ConcreteCreator1, info::String)
ConcreteProduct1(info)
end
# Implement the ConcreteProduct2,
# ConcreteCreator2 descendant structs
# and all required functions
Указание (PascalABC.NET). При определении конструктора класса ConcreteProduct2 вызывайте в его начале конструктор предка следующим образом: inherited Create(info) , при модификации метода Transform класса ConcreteProduct2 аналогичным образом вызывайте одноименный метод предка: inherited Transform() .
[PascalABC.NET] type
ConcreteProduct1 = class
protected
info: string;
public
function GetInfo: string;
begin
Result := info;
end;
constructor Create(info: string);
begin
// Реализуйте конструктор
end;
procedure Transform; virtual;
begin
// Реализуйте метод
end;
end;
// Реализуйте класс-потомок ConcreteProduct2
ConcreteCreator1 = class
protected
function FactoryMethod(info: string): ConcreteProduct1; virtual;
begin
Result := new ConcreteProduct1(info);
end;
public
function AnOperation(info: string): string;
begin
var p := FactoryMethod(info);
p.Transform;
p.Transform;
Result := p.GetInfo;
end;
end;
// Реализуйте класс-потомок ConcreteCreator2
OOP1Creat3°. Factory Method (Фабричный метод) — порождающий паттерн. Задание 3. Реализовать иерархию классов-животных с абстрактным предком Animal, содержащим метод GetInfo, который возвращает строковое значение, и шестью конкретными потомками: Lion, Tiger, Leopard (кошачьи, cats), Gorilla, Orangutan, Chimpanzee (человекообразные обезьяны, apes). Каждый конкретный класс содержит строковое поле name (имя животного), которое определяется в его конструкторе с помощью одноименного параметра. Метод GetInfo возвращает имя класса и имя животного, разделенные пробелом, например, «Lion Tom». Реализовать иерархию классов-создателей с абстрактным предком AnimalCreator и конкретными потомками CatCreator и ApeCreator. Фабричный метод CreateAnimal(ind, name) этих классов принимает параметр целого типа ind и строковый параметр name и возвращает объект типа Animal. Для конкретных классов CatCreator и ApeCreator параметр ind метода CreateAnimal определяет тип создаваемого животного по его индексу (0, 1 или 2) в группе кошачьих или обезьян, а параметр name — имя животного. В классе AnimalCreator также определить метод GetZoo с двумя параметрами-массивами inds и names одинакового размера; массив inds содержит целые числа, массив names — строки (предполагается, что элементы массива inds всегда имеют значения в диапазоне от 0 до 2). Метод GetZoo возвращает массив объектов Animal того же размера, что и массивы inds и names; каждый элемент полученного массива определяется с помощью фабричного метода с параметрами, равными значениям соответствующих элементов массивов inds и names. Тестирование разработанной системы классов. Дан набор из 4 пар (ind, name), где ind — целое число в диапазоне от 0 до 2, а name — строка. Сформировать массивы inds и names размера 4, содержащие числа и строки из исходного набора, и использовать их в методе GetZoo для создателей CatCreator и ApeCreator, получив в результате наборы кошачьих и обезьян размера 4. С помощью метода GetInfo вывести информацию о животных из каждого набора.
[C++] class Animal
{
public:
virtual string GetInfo() = 0;
virtual ~Animal()
{
Show("Animal");
}
};
class Lion : public Animal
{
string name;
public:
Lion(string name) : name(name) {}
string GetInfo()
{
return "Lion " + name;
}
};
// Implement the Tiger, Leopard, Gorilla,
// Orangutan and Chimpanzee descendant classes
class AnimalCreator
{
protected:
virtual shared_ptr<Animal> CreateAnimal(int ind,
string name) = 0;
public:
vector<shared_ptr<Animal>> GetZoo(vector<int> inds,
vector<string> names);
virtual ~AnimalCreator()
{
Show("AnimalCreator");
}
};
vector<shared_ptr<Animal>> AnimalCreator::GetZoo(vector<int> inds,
vector<string> names)
{
vector<shared_ptr<Animal>> zoo;
for (int i = 0; i < inds.size(); i++)
zoo.push_back(CreateAnimal(inds[i], names[i]));
return zoo;
}
// Implement the CatCreator and ApeCreator descendant classes
[C#] public abstract class Animal
{
public abstract string GetInfo();
}
public class Lion : Animal
{
string name;
public Lion(string name)
{
this.name = name;
}
public override string GetInfo()
{
return "Lion " + name;
}
}
// Implement the Tiger, Leopard, Gorilla,
// Orangutan and Chimpanzee descendant classes
public abstract class AnimalCreator
{
protected abstract Animal CreateAnimal(int ind, string name);
public Animal[] GetZoo(int[] inds, string[] names)
{
Animal[] zoo = new Animal[inds.Length];
for (int i = 0; i < zoo.Length; i++)
zoo[i] = CreateAnimal(inds[i], names[i]);
return zoo;
}
}
// Implement the CatCreator and ApeCreator descendant classes
[Java] import java.util.ArrayList;
abstract class Animal {
public abstract String getInfo();
}
class Lion extends Animal {
String name;
public Lion(String name) {
this.name = name;
}
public String getInfo() {
return "Lion " + name;
}
}
// Implement the Tiger, Leopard, Gorilla,
// Orangutan and Chimpanzee descendant classes
abstract class AnimalCreator {
protected abstract Animal createAnimal(int ind, String name);
public ArrayList<Animal> getZoo(int[] inds, String[] names) {
ArrayList<Animal> zoo = new ArrayList<Animal>();
for (int i = 0; i < inds.length; i++)
zoo.add(createAnimal(inds[i], names[i]));
return zoo;
}
}
// Implement the CatCreator and ApeCreator descendant classes
[Python] class Lion:
def __init__(self, name):
self.__name = name
def getInfo(self):
return "Lion " + self.__name
# Implement the Tiger, Leopard, Gorilla,
# Orangutan and Chimpanzee classes
class AnimalCreator:
def getZoo(self, inds, names):
zoo = []
for i in range(len(inds)):
zoo.append(self.createAnimal(inds[i], names[i]))
return zoo
class CatCreator(AnimalCreator):
def createAnimal(self, ind, name):
pass
# Implement the method
# Implement the ApeCreator descendant class
[Ruby] class Lion
def initialize(name)
@name = name
end
def getInfo
"Lion " + @name
end
end
# Implement the Tiger, Leopard, Gorilla,
# Orangutan and Chimpanzee classes
class AnimalCreator
def getZoo(inds, names)
zoo = []
inds.length.times do |i|
zoo << createAnimal(inds[i], names[i])
end
zoo
end
end
class CatCreator < AnimalCreator
def createAnimal(ind, name)
# Implement the method
end
end
# Implement the ApeCreator descendant class
[Julia] abstract type Animal
end
abstract type AnimalCreator
end
struct Lion <: Animal
name::String
end
# Implement the Tiger, Leopard, Gorilla,
# Orangutan and Chimpanzee descendant structs
# Implement the CatCreator and ApeCreator descendant structs
# and the createAnimal functions for them
function getInfo(a::Animal)
string(typeof(a)) * ' ' * a.name
end
function getZoo(c::AnimalCreator, inds::Vector{Int}, names::Vector{String})
zoo = []
for i in 1:length(inds)
push!(zoo, createAnimal(c, inds[i], names[i]))
end
zoo
end
[PascalABC.NET] type
Animal = abstract class
public
function GetInfo: string; abstract;
end;
Lion = class(Animal)
private
name: string;
public
constructor Create(name: string);
begin
self.name := name;
end;
function GetInfo: string; override;
begin
Result := 'Lion ' + name;
end;
end;
// Реализуйте классы-потомки Tiger, Leopard,
// Gorilla, Orangutan и Chimpanzee
AnimalCreator = abstract class
protected
function CreateAnimal(ind: integer; name: string): Animal; abstract;
public
function GetZoo(inds: array of integer; names: array of string): array of Animal;
begin
Result := new Animal[inds.Length];
for var i := 0 to inds.Length - 1 do
Result[i] := CreateAnimal(inds[i], names[i]);
end;
end;
// Реализуйте классы-потомки CatCreator и ApeCreator
OOP1Creat4°. 
Abstract Factory (Абстрактная фабрика) — порождающий паттерн. Известен также под именем Kit (Инструментарий). Частота использования: высокая. Назначение: предоставляет интерфейс для создания семейств взаимосвязанных или взаимозависимых объектов, не специфицируя конкретные классы этих объектов. Методы абстрактной фабрики обычно реализуются как фабричные методы (см. OOP1Creat1). Участники: • AbstractFactory (Абстрактная фабрика) — объявляет интерфейс для операций, создающих абстрактные объекты-продукты; • ConcreteFactory (Конкретная фабрика) — реализует операции, создающие конкретные объекты-продукты; • AbstractProduct (Абстрактный продукт) — объявляет интерфейс для типа объекта-продукта; • ConcreteProduct (Конкретный продукт) — определяет объект-продукт, создаваемый соответствующей конкретной фабрикой; реализует интерфейс AbstractProduct; • Client (Клиент) — пользуется исключительно интерфейсами, которые объявлены в классах AbstractFactory и AbstractProduct. Задание 1. Реализовать три иерархии классов, в одну из которых входят абстрактная фабрика AbstractFactory и две конкретные фабрики ConcreteFactory1 и ConcreteFactory2, в другую — абстрактный продукт (типа A) AbstractProductA и два его потомка ProductA1 и ProductA2, а в третью — абстрактный продукт (типа B) AbstractProductB и два его потомка ProductB1 и ProductB2. Все фабрики включают методы CreateProductA и CreateProductB, конкретные фабрики 1 и 2 возвращают конкретные продукты с соответствующим номером (фабрика 1 возвращает продукты ProductA1 и ProductB1, фабрика 2 — продукты ProductA2 и ProductB2). Все классы-продукты имеют метод GetInfo, возвращающий строковое значение. Кроме того, в продукте первого типа определен метод A без параметров, а в продукте второго типа — метод В с параметром типа AbstractProductA (методы не возвращают значений). Конкретные продукты содержат строковое поле info, которое инициализируется в конструкторе с помощью его параметра целого типа (в поле info записывается строковое представление целочисленного параметра конструктора). Метод GetInfo конкретных классов-продуктов возвращает текущее значение поля info. Для класса-продукта ProductA1 метод A переводит поле info в целое число, удваивает его и сохраняет строковое представление результата в поле info; для класса-продукта ProductA2 метод A просто удваивает строку info (например, строка «123» преобразуется в строку «123123»). Для класса-продукта ProductB1 вызов objB.B(objA) преобразует поля objB.info и objA.info в целые числа, складывает их и сохраняет строковое представление результата в поле objB.info; для класса-продукта ProductB2 вызов objB.B(objA) добавляет в конец строки objB.info строку objA.info и сохраняет результат в поле objB.info. Тестирование разработанной системы классов. Дано целое число Nf, которое может быть равно 1 или 2, целые числа Na и Nb и строка S, содержащая символы «A» и «B». Описать ссылочные переменные f типа AbstractFactory, pa типа AbstractProductA и pb типа AbstractProductB. Если число Nf равно 1, то связать f с конкретной фабрикой 1, если Nf равно 2, то связать f с конкретной фабрикой 2. Используя фабричные методы созданной фабрики, создать конкретные продукты типа A и B, инициализировав их данными числами Na и Nb соответственно, и связать их с переменными pa и pb. Затем для созданных продуктов pa и pb выполнить методы A и B в порядке, указанном в исходной строке S. При этом метод A должен вызываться для продукта pa, а метод B — для продукта pb, причем параметром метода B должен быть продукт pa. Используя методы GetInfo, вывести итоговые значения объектов-продуктов pa и pb (в указанном порядке). Примечание. При выполнении задания используются только ссылки на абстрактные классы, а также только методы, определенные в абстрактных классах (за исключением конструктора создаваемой конкретной фабрики), что и составляет суть паттерна «Абстрактная фабрика».
[C++] class AbstractProductA
{
public:
virtual void A() = 0;
virtual string GetInfo() = 0;
virtual ~AbstractProductA()
{
Show("AbstractProductA");
}
};
// Implement the ProductA1 and ProductA2 descendant classes
class AbstractProductB
{
public:
virtual void B(const AbstractProductA& objA) = 0;
virtual string GetInfo() = 0;
virtual ~AbstractProductB()
{
Show("AbstractProductB");
}
};
// Implement the ProductB1 and ProductB2 descendant classes
class AbstractFactory
{
public:
virtual shared_ptr<AbstractProductA> CreateProductA(int info)
= 0;
virtual shared_ptr<AbstractProductB> CreateProductB(int info)
= 0;
virtual ~AbstractFactory()
{
Show("AbstractFactory");
}
};
// Implement the ConcreteFactory1
// and ConcreteFactory2 descendant classes
[C#] public abstract class AbstractProductA
{
public abstract void A();
public abstract string GetInfo();
}
// Implement the ProductA1 and ProductA2 descendant classes
public abstract class AbstractProductB
{
public abstract void B(AbstractProductA objA);
public abstract string GetInfo();
}
// Implement the ProductB1 and ProductB2 descendant classes
public abstract class AbstractFactory
{
public abstract AbstractProductA CreateProductA(int info);
public abstract AbstractProductB CreateProductB(int info);
}
// Implement the ConcreteFactory1
// and ConcreteFactory2 descendant classes
[Java] abstract class AbstractProductA {
public abstract void A();
public abstract String getInfo();
}
// Implement the ProductA1 and ProductA2 descendant classes
abstract class AbstractProductB {
public abstract void B(AbstractProductA objA);
public abstract String getInfo();
}
// Implement the ProductB1 and ProductB2 descendant classes
abstract class AbstractFactory {
public abstract AbstractProductA createProductA(int info);
public abstract AbstractProductB createProductB(int info);
}
// Implement the ConcreteFactory1
// and ConcreteFactory2 descendant classes
[Python] class ProductA1:
def __init__(self, info):
pass
# Implement the "constructor"
def A(self):
pass
# Implement the method
def getInfo(self):
pass
# Implement the method
# Implement the ProductA2 class
class ProductB1:
def __init__(self, info):
pass
# Implement the "constructor"
def B(self, objA):
pass
# Implement the method
def getInfo(self):
pass
# Implement the method
# Implement the ProductB2 class
class ConcreteFactory1:
def createProductA(self, info):
pass
# Implement the method
def createProductB(self, info):
pass
# Implement the method
# Implement the ConcreteFactory2 class
[Ruby] class ProductA1
def initialize(info)
# Implement the "constructor"
end
def A
# Implement the method
end
def getInfo
# Implement the method
end
end
# Implement the ProductA2 class
class ProductB1
def initialize(info)
# Implement the "constructor"
end
def B(objA)
# Implement the method
end
def getInfo
# Implement the method
end
end
# Implement the ProductB2 class
class ConcreteFactory1
def createProductA(info)
# Implement the method
end
def createProductB(info)
# Implement the method
end
end
# Implement the ConcreteFactory2 class
[Julia] abstract type AbstractProductA
end
abstract type AbstractProductB
end
mutable struct ProductA1 <: AbstractProductA
info::String
end
mutable struct ProductA2 <: AbstractProductA
info::String
end
mutable struct ProductB1 <: AbstractProductB
info::String
end
mutable struct ProductB2 <: AbstractProductB
info::String
end
function getInfo(p::AbstractProductA)
p.info
end
function getInfo(p::AbstractProductB)
p.info
end
# Implement the constructors of the ProductA1,
# ProductA2, ProductB1 and ProductB2 structs
function A!(objA::ProductA1)
# Implement the function
end
function A!(objA::ProductA2)
# Implement the function
end
function B!(objB::ProductB1, objA::AbstractProductA)
# Implement the function
end
function B!(objB::ProductB2, objA::AbstractProductA)
# Implement the function
end
abstract type AbstractFactory
end
# Implement the ConcreteFactory1,
# ConcreteFactory2 descendant structs
# and the createProductA and createProductB
# functions for them
[PascalABC.NET] type
AbstractProductA = abstract class
public
procedure A; abstract;
function GetInfo: string; abstract;
end;
// Реализуйте классы-потомки ProductA1 и ProductA2
AbstractProductB = abstract class
public
procedure B(objA: AbstractProductA); abstract;
function GetInfo: string; abstract;
end;
// Реализуйте классы-потомки ProductB1 и ProductB2
type
AbstractFactory = abstract class
public
function CreateProductA(info: integer): AbstractProductA; abstract;
function CreateProductB(info: integer): AbstractProductB; abstract;
end;
// Реализуйте классы-потомки ConcreteFactory1 и ConcreteFactory2
OOP1Creat5°. Abstract Factory (Абстрактная фабрика) — порождающий паттерн. Задание 2. Реализовать иерархию классов, определяющих два вида элементов управления (controls): кнопки (buttons) и метки (labels). Абстрактные классы AbstractButton и AbstractLabel содержат метод GetControl, возвращающий строковое представление соответствующего элемента управления. Конкретные классы Button1, Button2, Label1, Label2 включают конструктор со строковым параметром text, который определяет текст, отображаемый на элементе управления (текст хранится в поле text). Конкретные классы отличаются видом строкового представления. Для Button1 и Label1 (первый вариант представления) текст отображается заглавными буквами, кнопки обрамляются квадратными скобками (например, [CAPTION]), метки обрамляются символами «=» (например, =MESSAGE=). Для Button2 и Label2 (второй вариант представления) текст отображается строчными буквами, кнопки обрамляются угловыми скобками (например, <caption>), метки обрамляются двойными кавычками (например, "message"). Реализовать иерархию классов ControlFactory (абстрактная фабрика), Factory1 и Factory2 (конкретные фабрики). Каждый класс содержит два метода: CreateButton(text) и CreateLabel(text). Для ControlFactory эти методы являются абстрактными, для конкретных фабрик они возвращают кнопку и метку соответствующего вида (первого или второго). Также реализовать класс Client, предназначенный для формирования набора элементов управления. Конструктор данного класса принимает параметр f типа ControlFactory, который в дальнейшем используется для генерации элементов управления требуемого типа. Класс Client включает метод AddButton(text) для добавления в набор новой кнопки, метод AddLabel(text) для добавления в набор новой метки и метод GetControls, возвращающий текстовое представление полученного набора элементов управления. В текстовом представлении каждый последующий элемент отделяется от предыдущего одним пробелом. Тестирование разработанной системы классов. Дано целое число N (≤ 6) и набор из N строк. Каждая строка начинается либо с символа «B» (признак кнопки), либо с символа «L» (признак метки). Затем идет пробел и текст соответствующего элемента управления. Используя два экземпляра класса Client, сформировать и вывести два варианта текстового представления указанного набора элементов управления. Вначале выводится первый вариант представления, затем второй.
[C++] class AbstractButton
{
public:
virtual string GetControl() = 0;
virtual ~AbstractButton()
{
Show("AbstractButton");
}
};
// Implement the Button1 and Button2 descendant classes
class AbstractLabel
{
public:
virtual string GetControl() = 0;
virtual ~AbstractLabel()
{
Show("AbstractLabel");
}
};
// Implement the Label1 and Label2 descendant classes
class ControlFactory
{
public:
virtual shared_ptr<AbstractButton> CreateButton(string text)
= 0;
virtual shared_ptr<AbstractLabel> CreateLabel(string text) = 0;
virtual ~ControlFactory()
{
Show("ControlFactory");
}
};
// Implement the Factory1 and Factory2 descendant classes
class Client
{
// Add required fields
public:
Client(shared_ptr<ControlFactory> f);
void AddButton(string text);
void AddLabel(string text);
string GetControls();
~Client()
{
Show("Client");
}
};
Client::Client(shared_ptr<ControlFactory> f)
{
// Implement the constructor
}
void Client::AddButton(string text)
{
// Implement the method
}
void Client::AddLabel(string text)
{
// Implement the method
}
string Client::GetControls()
{
return "";
// Remove the previous statement and implement the method
}
[C#] public abstract class AbstractButton
{
public abstract string GetControl();
}
// Implement the Button1 and Button2 descendant classes
public abstract class AbstractLabel
{
public abstract string GetControl();
}
// Implement the Label1 and Label2 descendant classes
public abstract class ControlFactory
{
public abstract AbstractButton CreateButton(string text);
public abstract AbstractLabel CreateLabel(string text);
}
// Implement the Factory1 and Factory2 descendant classes
public class Client
{
// Add required fields
public Client(ControlFactory f)
{
// Implement the constructor
}
public void AddButton(string text)
{
// Implement the method
}
public void AddLabel(string text)
{
// Implement the method
}
public string GetControls()
{
return "";
// Remove the previous statement and implement the method
}
}
[Java] abstract class AbstractButton {
public abstract String getControl();
}
// Implement the Button1 and Button2 descendant classes
abstract class AbstractLabel {
public abstract String getControl();
}
// Implement the Label1 and Label2 descendant classes
abstract class ControlFactory {
public abstract AbstractButton createButton(String text);
public abstract AbstractLabel createLabel(String text);
}
// Implement the Factory1 and Factory2 descendant classes
class Client {
// Add required fields
Client(ControlFactory f) {
// Implement the constructor
}
public void addButton(String text) {
// Implement the method
}
public void addLabel(String text) {
// Implement the method
}
public String getControls() {
return "";
// Remove the previous statement and implement the method
}
}
[Python] class Button1:
def __init__(self, text):
pass
# Implement the "constructor"
def getControl(self):
pass
# Implement the method
# Implement the Button2 class
class Label1:
def __init__(self, text):
pass
# Implement the "constructor"
def getControl(self):
pass
# Implement the method
# Implement the Label2 class
class Factory1:
def createButton(self, text):
pass
# Implement the method
def createLabel(self, text):
pass
# Implement the method
# Implement the Factory2 class
class Client:
def __init__(self, f):
pass
# Implement the "constructor"
def addButton(self, text):
pass
# Implement the method
def addLabel(self, text):
pass
# Implement the method
def getControls(self):
pass
# Implement the method
[Ruby] class Button1
def initialize(text)
# Implement the "constructor"
end
def getControl
# Implement the method
end
end
# Implement the Button2 class
class Label1
def initialize(text)
# Implement the "constructor"
end
def getControl
# Implement the method
end
end
# Implement the Label2 class
class Factory1
def createButton(text)
# Implement the method
end
def createLabel(text)
# Implement the method
end
end
# Implement the Factory2 class
class Client
def initialize(f)
# Implement the "constructor"
end
def addButton(text)
# Implement the method
end
def addLabel(text)
# Implement the method
end
def getControls
# Implement the method
end
end
Указание (Julia). При реализации конструкторов Button1 , Button2 , Label1 и Label2 используйте внутренний конструктор new , чтобы избежать бесконечной рекурсии.
[Julia] abstract type AbstractButton
end
abstract type AbstractLabel
end
abstract type ControlFactory
end
# Implement the Button1 and Button2 descendant structs
# Implement the Label1 and Label2 descendant structs
# Implement the Factory1 and Factory2 descendant structs
mutable struct Client
# Add required fields
end
# Implement the constructor of the Client struct
function addButton!(c::Client, text::String)
# Implement the function
end
function addLabel!(c::Client, text::String)
# Implement the function
end
function getControls(c::Client)
# Implement the function
end
[PascalABC.NET] type
AbstractButton = abstract class
public
function GetControl: string; abstract;
end;
// Реализуйте классы-потомки Button1 и Button2
AbstractLabel = abstract class
public
function GetControl: string; abstract;
end;
// Реализуйте классы-потомки Label1 и Label2
ControlFactory = abstract class
public
function CreateButton(text: string): AbstractButton; abstract;
function CreateLabel(text: string): AbstractLabel; abstract;
end;
// Реализуйте классы-потомки Factory1 и Factory2
Client = class
private
// Добавьте требуемые поля
public
constructor Create(f: ControlFactory);
begin
// Реализуйте конструктор
end;
procedure AddButton(text: string);
begin
// Реализуйте метод
end;
procedure AddLabel(text: string);
begin
// Реализуйте метод
end;
function GetControls: string;
begin
Result := '';
// Удалите предыдущий оператор и реализуйте метод
end;
end;
Singleton, Prototype, Builder
OOP1Creat6°. 
Singleton (Одиночка) — порождающий паттерн. Частота использования: выше средней. Назначение: гарантирует, что у класса есть только один экземпляр, и предоставляет к нему глобальную точку доступа. Участники: • Singleton (Одиночка) — определяет операцию Instance, которая позволяет клиентам получать доступ к единственному экземпляру (операция Instance обычно оформляется в виде статического, т. е. классового, метода); может обеспечивать отложенную инициализацию данного экземпляра. Чтобы проиллюстрировать особенности паттерна Singleton, в задании предлагается реализовать не только его стандартный вариант, но и основанные на той же идее варианты, допускающие использование ограниченного количества экземпляров. Задание 1. Реализовать иерархию классов, включающую абстрактный базовый класс BaseClass и классы-потомки Singleton, Doubleton и Tenton, обеспечивающие создание ограниченного количества своих экземпляров. Класс BaseClass включает целочисленное поле data и два связанных с ним метода: метод IncData(increment) увеличивает значение data на величину целочисленного параметра increment, метод GetData без параметров возвращает текущее значение поля data. Поле data инициализируется нулевым значением. Класс Singleton реализует стандартный паттерн «Одиночка». Он включает статическое поле uniqueInstance — ссылку на тип Singleton (инициализируется нулевой ссылкой), закрытый конструктор, не выполняющий дополнительных действий, и статический метод Instance без параметров, который возвращает ссылку на объект типа Singleton). Метод Instance выполняет следующие действия: если поле uniqueInstance является нулевой ссылкой, то метод создает объект Singleton, помещает ссылку на него в поле uniqueInstance и возвращает эту ссылку как результат своей работы; если поле uniqueInstance уже содержит ссылку на объект Singleton, то метод Instance просто возвращает эту ссылку. Дополнительно реализовать статический метод InstanceCount (без параметров), который возвращает 0, если поле uniqueInstance содержит нулевую ссылку, и 1 в противном случае. Классы Doubleton и Tenton реализуют вариант паттерна, допускающий использование не более двух и не более десяти экземпляров соответственно. Класс Doubleton включает статический массив instances из двух элементов-ссылок типа Doubleton (элементы инициализируются нулевыми значениями), закрытый конструктор и статические методы Instance1 и Instance2, которые выполняют действия, аналогичные действиям метода Instance класса Singleton, но обращаются соответственно к элементам массива instances с индексами 0 и 1. Класс Tenton отличается от класса Doubleton тем, что его статический массив instances содержит 10 элементов-ссылок типа Tenton, а вместо двух методов Instance1 и Instance2 он включает статический метод Instance(index) с целочисленным параметром, определяющим индекс элемента массива instances, к которому обращается данный метод. Если параметр находится вне диапазона 0–9, то метод Instance может возвращать нулевую ссылку или возбуждать исключение (при выполнении задания такая ситуация не будет возникать). В классах Doubleton и Tenton дополнительно реализовать статический метод InstanceCount (без параметров), который возвращает количество элементов массива instances, не являющихся нулевыми ссылками. Тестирование разработанной системы классов. Дано целое число N (≤ 10) и набор из N строк, которые могут принимать значения «S», «D1», «D2», «T0», «T1», …, «T9». Создать массив b из N элементов — ссылок на BaseClass и инициализировать его элементы экземплярами классов Singleton, Doubleton, Tenton, используя следующие варианты статических методов в зависимости от значения соответствующей строки из исходного набора: для строки «S» используется метод Instance класса Singleton; для строк «D1», «D2» — соответственно методы Instance1 и Instance2 класса Doubleton; для строк «T0», «T1», …, «T9» — метод Instance(index) класса Tenton с параметром index, соответствующим цифре, указанной в строке. После создания всех элементов массива b вывести значения метода InstanceCount для классов Singleton, Doubleton, Tenton в указанном порядке. Также дано целое число K (≤ 20) и набор из K пар целых чисел (index, increment), в котором число index находится в диапазоне от 0 до N − 1 и определяет индекс элемента в массиве b, а число increment определяет параметр метода IncData, который надо вызвать для элемента массива b с индексом index. После вызова всех требуемых методов IncData вывести итоговые значения поля data для всех объектов массива b, используя метод GetData.
Примечание 1 (C++). В языке C++ для описания статических членов класса (полей и методов) используется модификатор static ; при этом поля обычно делаются закрытыми (private), а методы открытыми (public). Определение начальных значений статических полей выполняется после определения класса, причем перед именем поля надо указать имя класса, отделив его от имени поля символами :: (аналогично определяются и методы). При определении полей и методов модификатор static уже не указывается. При вызове статических методов перед ними можно указывать имя объекта, но обычно вместо этого указывается имя класса, которое отделяется от имени метода символами :: .
Примечание 2 (C++). При реализации класса Singleton (и аналогичных ему) в языке С++ необходимо объявить закрытым не только конструктор, но и деструктор класса. Это, в частности, не позволит внешней программе применить операцию delete к возвращенному результату функции Instance (и тем самым разрушить статический объект uniqueInstance ). Кроме того, наличие закрытого деструктора делает невозможным копирование объекта (и тем самым создание еще одного его экземпляра).
Примечание 3 (C++). Наличие в классе закрытых конструктора и деструктора не позволяет использовать «умные» указатели shared_ptr для объектов этого класса, поэтому в данном задании следует применять обычные указатели и операции new –delete .
[C++] class BaseClass
{
int data = 0;
public:
void IncData(int increment);
int GetData();
};
void BaseClass::IncData(int increment)
{
data += increment;
}
int BaseClass::GetData()
{
return data;
}
class Singleton : public BaseClass
{
static Singleton* uniqueInstance;
Singleton() {}
~Singleton()
{
Show("Singleton");
}
public:
// Complete the implementation of the class
};
Singleton* Singleton::uniqueInstance = nullptr;
class Doubleton : public BaseClass
{
static Doubleton* instances[];
Doubleton() {}
~Doubleton()
{
Show("Doubleton");
}
public:
// Complete the implementation of the class
};
Doubleton* Doubleton::instances[2];
class Tenton : public BaseClass
{
static Tenton* instances[];
Tenton() {}
~Tenton()
{
Show("Tenton");
}
public:
// Complete the implementation of the class
};
Tenton* Tenton::instances[10];
Указание (C#). Для языка C# задачник Programming Taskbook выполняет тестирование при однократном запуске программы путем многократного вызова ее функции Solve . Так как между вызовами функции Solve содержимое статических полей классов сохраняется, в случае данного задания это приведет к ошибочным результатам, начиная со второго тестового испытания. Чтобы избежать сообщения об ошибке, следует реализовать в классах Singleton , Doubleton и Tenton вспомогательный статический метод Reset , который обнуляет все статические ссылки (uniqueInstance для класса Singleton , элементы массива instances для классов Doubleton и Tenton ), и вызывать методы Reset для этих классов после вывода всех результирующих данных.
Примечание (C#). В языке C# для описания статических членов класса (полей и методов) используется модификатор static ; при этом поля обычно делаются закрытыми (private), а методы открытыми (public). При описании статических полей (как и обычных) можно сразу задавать их начальные значения. Класс может содержать особый статический конструктор (без параметров), который вызывается автоматически. При вызове статических методов перед ними надо указывать не имя какого-либо объекта, а имя класса, которое отделяется от имени метода точкой.
[C#] public abstract class BaseClass
{
int data;
public void IncData(int increment)
{
data += increment;
}
public int GetData()
{
return data;
}
}
public class Singleton : BaseClass
{
static Singleton uniqueInstance;
Singleton() {}
public static void Reset()
{
uniqueInstance = null;
}
// Complete the implementation of the class
}
public class Doubleton : BaseClass
{
static Doubleton[] instances = new Doubleton[2];
Doubleton() {}
public static void Reset()
{
instances[0] = instances[1] = null;
}
// Complete the implementation of the class
}
public class Tenton : BaseClass
{
static Tenton[] instances = new Tenton[10];
Tenton() {}
public static void Reset()
{
for (int i = 0; i < instances.Length; i++)
instances[i] = null;
}
// Complete the implementation of the class
}
Указание (Java). Для языка Java задачник Programming Taskbook выполняет тестирование при однократном запуске программы путем многократного вызова ее функции solve . Так как между вызовами функции solve содержимое статических полей классов сохраняется, в случае данного задания это приведет к ошибочным результатам, начиная со второго тестового испытания. Чтобы избежать сообщения об ошибке, следует реализовать в классах Singleton , Doubleton и Tenton вспомогательный статический метод reset , который обнуляет все статические ссылки (uniqueInstance для класса Singleton , элементы массива instances для классов Doubleton и Tenton ), и вызывать методы reset для этих классов после вывода всех результирующих данных.
Примечание (Java). В языке Java для описания статических членов класса (полей и методов) используется модификатор static ; при этом поля обычно делаются закрытыми (private), а методы открытыми (public). При описании статических полей (как и обычных) можно сразу задавать их начальные значения. При вызове статических методов перед ними можно указывать имя объекта, но можно указывать и имя класса, которое, как и имя объекта, отделяется от имени метода точкой.
[Java] class BaseClass {
private int data = 0;
public void incData(int increment) {
this.data += increment;
}
public int getData() {
return data;
}
}
class Singleton extends BaseClass {
private static Singleton uniqueInstance = null;
private Singleton() {}
// Complete the implementation of the class
}
class Doubleton extends BaseClass {
private static Doubleton instances[] = new Doubleton[2];
private Doubleton() {}
// Complete the implementation of the class
}
class Tenton extends BaseClass {
private static Tenton instances[] = new Tenton[10];
private Tenton() {}
// Complete the implementation of the class
}
Указание (Python). Для языка Python задачник Programming Taskbook выполняет тестирование при однократном запуске программы путем многократного вызова ее функции solve . Так как между вызовами функции solve содержимое статических полей классов сохраняется, в случае данного задания это приведет к ошибочным результатам, начиная со второго тестового испытания. Чтобы избежать сообщения об ошибке, следует реализовать в классах Singleton , Doubleton и Tenton вспомогательный статический метод reset , который обнуляет все статические ссылки (uniqueInstance для класса Singleton , элементы массива instances для классов Doubleton и Tenton ), и вызывать методы reset для этих классов после вывода всех результирующих данных.
Примечание 1 (Python). В языке Python для определения статических полей, т. е. полей, относящихся к классу в целом, достаточно описать и инициализировать эти поля внутри описания класса (не указывая перед ними слово self , в отличие от обычных полей при доступе к ним из обычных методов). Статические поля, как и обычные, можно сделать закрытыми, указав в начале их имени два символа подчеркивания. Что касается методов, то в языке Python есть два вида методов, относящихся к классу в целом: это собственно статические методы, перед определением которых указывается модификатор @staticmethod , и классовые методы, перед определением которых указывается модификатор @classmethod . Статические методы в Python — это методы, определяемые внутри класса и не имеющие специальных параметров. У классовых методов первый параметр имеет особый смысл — это передаваемый ему класс (обычно этот параметр имеет имя cls ). С помощью параметра cls можно обращаться к статическим полям класса, например, cls.__static_field (подобно тому, как с помощью параметра self обычных методов можно обращаться к полям объекта). В обычных методах тоже можно обращаться к статическим полям; для доступа к ним надо использовать параметр self , например, self.__static_field ). При вызове как статических, так и классовых методов перед ними надо указывать не имя какого-либо объекта, а имя класса, которое отделяется от имени метода точкой. При этом для классовых методов указанное имя класса считается первым параметром метода (параметром cls ).
Примечание 2 (Python). Поскольку в языке Python нельзя определить закрытый конструктор, необходимо предусмотреть специальные средства, делающие невозможным вызов конструктора из внешней программы. Для этого можно поступить следующим образом: определить закрытое статическое поле __private_key , которое инициализируется некоторым объектом (недоступным внешней программе), и снабдить метод __init__ параметром private_key . Вызов конструктора будет считаться успешным только в случае, если ему в качестве этого параметра будет передан объект __private_key , а это может сделать только какой-либо метод класса, так как вне класса доступ к полю __private_key невозможен.
[Python] class BaseClass:
def __init__(self):
self.__data = 0
def incData(self, increment):
self.__data += increment
def getData(self):
return self.__data
class Singleton(BaseClass):
__private_key = object()
__uniqueInstance = None
def __init__(self, private_key):
if private_key != self.__private_key:
raise Exception("Error")
super().__init__()
# Complete the implementation of the class
class Doubleton(BaseClass):
__private_key = object()
__instances = [None] * 2
def __init__(self, private_key):
if private_key != self.__private_key:
raise Exception("Error")
super().__init__()
# Complete the implementation of the class
class Tenton(BaseClass):
__private_key = object()
__instances = [None] * 10
def __init__(self, private_key):
if private_key != self.__private_key:
raise Exception("Error")
super().__init__()
# Complete the implementation of the class
Указание (Ruby). Для языка Ruby задачник Programming Taskbook выполняет тестирование при однократном запуске программы путем многократного вызова ее функции solve . Так как между вызовами функции solve содержимое статических полей классов сохраняется, в случае данного задания это приведет к ошибочным результатам, начиная со второго тестового испытания. Чтобы избежать сообщения об ошибке, следует реализовать в классах Singleton , Doubleton и Tenton вспомогательный статический метод reset , который обнуляет все статические ссылки (uniqueInstance для класса Singleton , элементы массива instances для классов Doubleton и Tenton ), и вызывать методы reset для этих классов после вывода всех результирующих данных.
Примечание (Ruby). В языке Ruby в именах статических полей используется особый префикс @@ , а при описании статических методов перед их именами указывается self и точка (например, self.instanceCount ). При описании статических полей (как и обычных) можно сразу задавать их начальные значения. При вызове статических методов перед ними надо указывать не имя какого-либо объекта, а имя класса, которое отделяется от имени метода точкой.
[Ruby] class BaseClass
def initialize
@data = 0
end
def incData(increment)
@data += increment
end
def getData
@data
end
end
class Singleton < BaseClass
@@uniqueInstance = nil
# Complete the implementation of the class
private_class_method :new
end
class Doubleton < BaseClass
@@instances = Array.new(2)
# Complete the implementation of the class
private_class_method :new
end
class Tenton < BaseClass
@@instances = Array.new(10)
# Complete the implementation of the class
private_class_method :new
end
Примечание 1 (Julia). В Julia нет статических полей и статических методов. Вместо статических полей необходимо использовать глобальные переменные. В данном случае можно использовать переменную uniqueInstance для структуры Singleton и массивы instancesD и instancesT для структур Doubleton и Tenton соответственно. Для статических методов надо использовать обычные функции с первым параметром вида ::Type{TypeName} (например, instance(::Type{Singleton} ). При вызове функции в качестве первого параметра указывается конкретный тип (например, instance(Singleton) ).
Примечание 2 (Julia). Поскольку по условию задачи в начале работы программы глобальные переменные (играющие роль статических полей) не должны связываться с конкретными объектами, их следует создать как ссылочные объекты требуемого типа (Ref{TypeName}() , например, Ref{Singleton}() ). Для таких объектов определена функция isassigned , позволяющая проверить, связана ли данная ссылка с конкретным объектом. Если со ссылкой связан объект, то получить доступ к нему можно с помощью операции [] .
Указание 1 (Julia). При решении задач на языке Julia необходимо учитывать, что в этом языке массивы индексируются от 1, и соответствующим образом корректировать индексы, передаваемые в качестве исходных данных.
Указание 2 (Julia). Поскольку в Julia конкретные типы не могут наследоваться, а по условию задачи тип BaseClass должен быть типом-предком, его надо описать как абстрактный тип, не содержащий полей. Поэтому поле data необходимо добавить в конкретные структуры Singleton , Doubleton и Tenton .
Указание 3 (Julia). Для языка Julia задачник Programming Taskbook выполняет тестирование при однократном запуске программы путем многократного вызова ее функции solve . Так как между вызовами функции solve содержимое глобальных полей сохраняется, в случае данного задания это приведет к ошибочным результатам, начиная со второго тестового испытания. Чтобы избежать сообщения об ошибке, следует реализовать для структур Singleton , Doubleton и Tenton вспомогательную функцию reset , которая обнуляет все глобальные ссылки (uniqueInstance для структуры Singleton , элементы массива instancesD и instancesT для структур Doubleton и Tenton ), и вызывать функции reset для этих структур после вывода всех результирующих данных. Поскольку эти функции играют роль статических методов, их параметр должен иметь вид ::Type{TypeName} (см. примечание 1).
[Julia] abstract type BaseClass end
mutable struct Singleton <: BaseClass
data::Int
end
mutable struct Doubleton <: BaseClass
data::Int
end
mutable struct Tenton <: BaseClass
data::Int
end
uniqueInstance = Ref{Singleton}()
instancesD = [Ref{Doubleton}() for i = 1:2]
instancesT = [Ref{Tenton}() for i = 1:10]
Singleton() = Singleton(0)
Doubleton() = Doubleton(0)
Tenton() = Tenton(0)
function incData!(b::BaseClass, increment::Int)
b.data += increment
end
function getData(b::BaseClass)
b.data
end
function instance(::Type{Singleton})
global uniqueInstance
if !isassigned(uniqueInstance)
uniqueInstance[] = Singleton()
end
uniqueInstance[]
end
# Implement the required functions
# (instance for Tenton,
# instance1 and instance2 for Doubleton,
# instanceCount and reset for Singleton,
# Doubleton and Tenton)
Указание (PascalABC.NET). Для языка PascalABC.NET задачник Programming Taskbook выполняет тестирование при однократном запуске программы путем многократного выполнения ее основного блока операторов begin-end . Так как при многократном выполнении основного блока содержимое статических полей классов сохраняется, в случае данного задания это приведет к ошибочным результатам, начиная со второго тестового испытания. Чтобы избежать сообщения об ошибке, следует реализовать в классах Singleton , Doubleton и Tenton вспомогательный статический метод Reset , который обнуляет все статические ссылки (uniqueInstance для класса Singleton , элементы массива instances для классов Doubleton и Tenton ), и вызывать методы Reset для этих классов после вывода всех результирующих данных.
Примечание (PascalABC.NET). В языке PascalABC.NET для описания статических членов класса (полей и методов) используется модификатор static ; при этом поля обычно делаются закрытыми (private), а методы открытыми (public). Модификатор static указывается перед описанием поля, метода или конструктора. При описании статических полей (как и обычных) можно сразу задавать их начальные значения. Класс может содержать особый статический конструктор (без параметров), который вызывается автоматически. При вызове статических методов перед ними надо указывать не имя какого-либо объекта, а имя класса, которое отделяется от имени метода точкой.
[PascalABC.NET]
type
BaseClass = class
private
data: integer;
public
procedure IncData(increment: integer);
begin
data := data + increment;
end;
function GetData: integer;
begin
Result := data;
end;
end;
Singleton = class(BaseClass)
private
static uniqueInstance: Singleton;
constructor Create;
begin
inherited Create;
end;
public
static procedure Reset;
begin
uniqueInstance := nil;
end;
// Завершите реализацию класса
end;
Doubleton = class(BaseClass)
private
static instances := new Doubleton[2];
constructor Create;
begin
inherited Create;
end;
public
static procedure Reset;
begin
instances[0] := nil;
instances[1] := nil;
end;
// Завершите реализацию класса
end;
Tenton = class(BaseClass)
private
static instances := new Tenton[10];
constructor Create;
begin
inherited Create;
end;
public
static procedure Reset;
begin
for var i := 0 to instances.Length - 1 do
instances[i] := nil;
end;
// Завершите реализацию класса
end;
OOP1Creat7°. 
Prototype (Прототип) — порождающий паттерн. Частота использования: средняя. Назначение: задает виды создаваемых объектов с помощью экземпляра-прототипа и создает новые объекты путем копирования («клонирования») этого прототипа. Участники: • Prototype (Прототип) — объявляет интерфейс для клонирования самого себя; • ConcretePrototype (Конкретный прототип) — реализует операцию клонирования себя; • Client (Клиент) — создает новый объект, обращаясь к прототипу с запросом клонировать себя. Задание 1. Реализовать иерархию классов, которая содержит абстрактный прототип Prototype и два конкретных прототипа ConcretePrototype1 и ConcretePrototype2. Все классы включают метод Clone без параметров, возвращающий копию объекта, вызвавшего данный метод, а также методы GetInfo и ChangeId. Метод GetInfo без параметров возвращает строку, метод ChangeId имеет целочисленный параметр id и ничего не возвращает. В классе Prototype методы Clone, GetInfo и ChangeId являются абстрактными. Конкретные прототипы содержат строковое поле data и целочисленное поле id, которые инициализируются одноименными параметрами конструктора. Метод GetInfo для конкретных прототипов возвращает строку, содержащую краткое имя типа («CP1» для типа ConcretePrototype1 и «CP2» для типа ConcretePrototype2) и значения полей data и id (части описания разделяются символом «=», например, «CP1=TEXT=34»). Метод ChangeId изменяет значение поля id. При реализации метода Clone можно использовать специальные средства стандартной библиотеки или обычный вызов конструктора. Также реализовать класс Client, предназначенный для работы с группой объектов типа ConcretePrototype1 или ConcretePrototype2. Конструктор класса Client имеет параметр-ссылку типа Prototype, определяющий прототип объектов, включаемых в группу (прототип в группу не входит и сохраняется в специальном поле prot; для хранения группы объектов удобно использовать динамическую структуру). Класс Client также содержит методы Operation(id) и GetObjects. Метод Operation добавляет в набор новый объект, получаемый путем клонирования прототипа и последующего изменения поля id полученного объекта в соответствии со значением параметра метода Operation. Метод GetObjects без параметров возвращает строку с описанием всех объектов группы (описания разделяются пробелом). Все объекты создаваемой группы имеют одно и то же поле data и различные поля id (задаваемые в методе Operation). Тестирование разработанной системы классов. Дана строка S, целое число N (≤ 10) и набор из N целых чисел. Вызывая метод Operation для двух объектов класса Client, сформировать два набора из N объектов. Первый набор содержит объекты типа ConcretePrototype1 (и создается первым объектом Client), второй набор содержит объекты типа ConcretePrototype2 (и создается вторым объектом Client). Все созданные объекты должны иметь одинаковые поля data, равные строке S, и значения полей id, взятые из исходного набора целых чисел. Используя метод GetObjects, вывести строковые описания каждого из полученных наборов объектов. Примечание. В реальной ситуации подход, основанный на паттерне «Прототип» будет эффективным, если объем действий по обычному созданию объекта превосходит объем действий по его клонированию и последующей настройке некоторых свойств.
[C++] class Prototype
{
public:
virtual shared_ptr<Prototype> Clone() = 0;
virtual void ChangeId(int id) = 0;
virtual string GetInfo() = 0;
virtual ~Prototype()
{
Show("Prototype");
}
};
// Implement the ConcretePrototype1
// and ConcretePrototype2 descendant classes
class Client
{
// Add required fields
public:
Client(shared_ptr<Prototype> p);
void Operation(int id);
string GetObjects();
~Client()
{
Show("Client");
}
};
Client::Client(shared_ptr<Prototype> p)
{
// Implement the constructor
}
void Client::Operation(int id)
{
// Implement the method
}
string Client::GetObjects()
{
return "";
// Remove the previous statement and implement the method
}
[C#] public abstract class Prototype
{
public abstract Prototype Clone();
public abstract void ChangeId(int id);
public abstract string GetInfo();
}
// Implement the ConcretePrototype1
// and ConcretePrototype2 descendant classes
public class Client
{
// Add required fields
public Client(Prototype p)
{
// Implement the constructor
}
public void Operation(int id)
{
// Implement the method
}
public string GetObjects()
{
return "";
// Remove the previous statement and implement the method
}
}
[Java] abstract class Prototype extends Cloneable {
public abstract Prototype clone();
public abstract void changeId(int id);
public abstract String getInfo();
}
// Implement the ConcretePrototype1
// and ConcretePrototype2 descendant classes
class Client {
// Add required fields
public Client(Prototype p) {
// Implement the constructor
}
public void operation(int id) {
// Implement the method
}
public String getObjects() {
return "";
// Remove the previous statement and implement the method
}
}
[Python] class ConcretePrototype1:
def __init__(self, str):
pass
# Implement the "constructor"
def clone(self):
pass
# Implement the method
def changeId(self, id):
pass
# Implement the method
def getInfo(self):
pass
# Implement the method
# Implement the ConcretePrototype2 class
class Client:
def __init__(self, p):
pass
# Implement the "constructor"
def operation(self, id):
pass
# Implement the method
def getObjects(self):
pass
# Implement the method
Указание (Ruby). Все классы в языке Ruby имеют встроенную реализацию метода clone , поэтому описывать этот метод при решении задачи не требуется: достаточно использовать существующую реализацию.
[Ruby] class ConcretePrototype1
def initialize(str)
# Implement the "constructor"
end
def changeId(id)
# Implement the method
end
def getInfo
# Implement the method
end
end
# Implement the ConcretePrototype2 class
class Client
def initialize(p)
# Implement the "constructor"
end
def operation(id)
# Implement the method
end
def getObjects
# Implement the method
end
end
Указание (Julia). При реализации функции clone в языке Julia достаточно использовать стандартную функцию deepcopy .
[Julia] abstract type Prototype
end
function clone(p::Prototype)
deepcopy(p)
end
# Implement the ConcretePrototype1,
# ConcretePrototype2 descendant structs
# and the required functions for them
# (changeId and getInfo)
struct Client
# Add required fields
end
# Implement the constructor of the Client struct
function operation(c::Client, id::Int)
# Implement the function
end
function getObjects(c::Client)
# Implement the function
end
[PascalABC.NET] type
Prototype = abstract class
public
function Clone: Prototype; abstract;
procedure ChangeId(id: integer); abstract;
function GetInfo: string; abstract;
end;
// Реализуйте классы-потомки ConcretePrototype1 и ConcretePrototype1
Client = class
// Добавьте требуемые поля
public
constructor Create(p: Prototype);
begin
// Реализуйте конструктор
end;
procedure Operation(id: integer);
begin
// Реализуйте метод
end;
function GetObjects: string;
begin
Result := '';
// Удалите предыдущий оператор и реализуйте метод
end;
end;
OOP1Creat8°. Prototype (Прототип) — порождающий паттерн. Задание 2. Реализовать иерархию классов, связанных с графическими примитивами: AbstractGraphic (абстрактный предок), Ellip, Line и Rect (конкретные примитивы). Классы содержат метод Clone без параметров, возвращающий копию объекта, вызвавшего данный метод, а также метод ChangeLocation(x1, y1, x2, y2) с целочисленными параметрами x1, y1, x2, y2, не возвращающий значений, и метод Draw без параметров, возвращающий строку. В классе AbstractGraphic методы Clone, ChangeLocation и Draw являются абстрактными. Классы конкретных примитивов содержат целочисленные поля x1, y1, x2, y2; по умолчанию они инициализируются нулями. Метод ChangeLocation изменяет эти поля, а метод Draw возвращает строку, содержащую имя класса и текущие значения полей без пробелов (например, «Line(1,3,-1,5)»). При реализации метода Clone можно использовать специальные средства стандартной библиотеки или обычный вызов конструктора. Также реализовать класс GraphEditor, предназначенный для работы с графическими объектами. В конструктор класса GraphEditor передаются два ссылочных параметра p0, p1 типа AbstractGraphic, определяющих прототипы создаваемых объектов-примитивов. Для хранения прототипов используется массив из двух элементов, для хранения созданного набора графических примитивов удобно использовать динамическую структуру. Класс GraphEditor включает два метода. Метод AddGraphic(ind, x1, y1, x2, y2) добавляет в набор графических примитивов объект, созданный на основе прототипа с индексом ind (0 или 1) и устанавливает для него указанные координаты. Метод DrawAll без параметров возвращает строковое описание всех добавленных графических объектов, используя их метод Draw (описания объектов разделяются пробелом). Тестирование разработанной системы классов. Дана двухсимвольная строка P, содержащая две различные буквы из набора «E», «L», «R». Также дано целое число N (≤ 5) и набор из N пятерок целых чисел вида (ind, x1, y1, x2, y2), где ind принимает значение 0 или 1, а остальные числа являются произвольными. Создать объект GraphEditor, инициализировав его двумя прототипами, которые соответствуют символам строки P («E» — Ellip, «L» — Line, «R» — Rect); порядок прототипов определяется порядком символов в строке P. Добавить в набор графических примитивов N объектов, используя вызовы метода AddGraphic с параметрами, определяемыми пятерками данных чисел, и вывести полученный набор примитивов методом DrawAll.
[C++] class AbstractGraphic
{
public:
virtual shared_ptr<AbstractGraphic> Clone() = 0;
virtual void ChangeLocation(int x1, int y1, int x2,
int y2) = 0;
virtual string Draw() = 0;
virtual ~AbstractGraphic()
{
Show("AbstractGraphic");
}
};
// Implement the Ellip, Line and Rect descendant classes
class GraphEditor
{
// Add required fields
public:
GraphEditor(shared_ptr<AbstractGraphic> p0,
shared_ptr<AbstractGraphic> p1);
void AddGraphic(int ind, int x1, int y1, int x2, int y2);
string DrawAll();
~GraphEditor()
{
Show("GraphEditor");
}
};
GraphEditor::GraphEditor(shared_ptr<AbstractGraphic> p0,
shared_ptr<AbstractGraphic> p1)
{
// Implement the constructor
}
void GraphEditor::AddGraphic(int ind, int x1, int y1,
int x2, int y2)
{
// Implement the method
}
string GraphEditor::DrawAll()
{
return "";
// Remove the previous statement and implement the method
};
[C#] public abstract class AbstractGraphic
{
public abstract AbstractGraphic Clone();
public abstract void ChangeLocation(int x1, int y1,
int x2, int y2);
public abstract string Draw();
}
// Implement the Ellip, Line and Rect descendant classes
public class GraphEditor
{
// Add required fields
public GraphEditor(AbstractGraphic p0, AbstractGraphic p1)
{
// Implement the constructor
}
public void AddGraphic(int ind, int x1, int y1, int x2, int y2)
{
// Implement the method
}
public string DrawAll()
{
return "";
// Remove the previous statement and implement the method
}
}
[Java] abstract class AbstractGraphic extends Cloneable {
public abstract AbstractGraphic clone();
public abstract void changeLocation(int x1, int y1,
int x2, int y2);
public abstract String draw();
}
// Implement the Ellip, Line and Rect descendant classes
class GraphEditor {
// Add required fields
public GraphEditor(AbstractGraphic p0, AbstractGraphic p1) {
// Implement the constructor
}
public void addGraphic(int ind, int x1, int y1,
int x2, int y2) {
// Implement the method
}
public String drawAll() {
return "";
// Remove the previous statement and implement the method
}
}
[Python] class Ellip:
def __init__(self):
pass
# Implement the "constructor"
def changeLocation(self, x1, y1, x2, y2):
pass
# Implement the method
def draw(self):
pass
# Implement the method
# Implement the Line and Rect classes
class GraphEditor:
def __init__(self, p0, p1):
pass
# Implement the "constructor"
def addGraphic(self, ind, x1, y1, x2, y2):
pass
# Implement the method
def drawAll(self):
pass
# Implement the method
Указание (Ruby). См. указание к заданию OOP1Creat7.
[Ruby] class Ellip
def initialize
# Implement the "constructor"
end
def changeLocation(x1,y1,x2,y2)
# Implement the method
end
def draw
# Implement the method
end
end
# Implement the Line and Rect classes
class GraphEditor
def initialize(p0,p1)
# Implement the "constructor"
end
def addGraphic(ind,x1,y1,x2,y2)
# Implement the method
end
def drawAll
# Implement the method
end
end
Указание (Julia). Чтобы получить имя типа для объекта g в виде строки, достаточно использовать выражение string(typeof(g)) . Это позволяет реализовать единственный вариант функции draw , указав в качестве ее параметра g::AbstractGraphic .
[Julia] abstract type AbstractGraphic
end
function clone(g::AbstractGraphic)
deepcopy(g)
end
# Implement the Ellip, Line
# and Rect descendant structs
# and the required functions for them
# (changeLocation and draw)
struct GraphEditor
# Add required fields
end
# Implement the constructor of the GraphEditor struct
function addGraphic(e::GraphEditor, ind::Int, x1::Int, y1::Int, x2::Int, y2::Int)
# Implement the function
end
function drawAll(e::GraphEditor)
# Implement the function
end
[PascalABC.NET] type
AbstractGraphic = abstract class
public
function Clone: AbstractGraphic; abstract;
procedure ChangeLocation(x1, y1, x2, y2: integer); abstract;
function Draw: string; abstract;
end;
// Реализуйте классы-потомки Ellip,
// Line и Rect
GraphEditor = class
private
// Добавьте требуемые поля
public
constructor Create(p0, p1: AbstractGraphic);
begin
// Реализуйте конструктор
end;
procedure AddGraphic(ind, x1, y1, x2, y2: integer);
begin
// Реализуйте метод
end;
function DrawAll: string;
begin
Result := '';
// Удалите предыдущий оператор и реализуйте метод
end;
end;
OOP1Creat9°. 
Builder (Строитель) — порождающий паттерн. Частота использования: ниже средней. Назначение: отделяет конструирование сложного объекта от его представления, так что в результате одного и того же процесса конструирования могут получаться разные представления. Участники: • Builder (Строитель) — задает абстрактный интерфейс для создания частей объекта Product; • ConcreteBuilder (Конкретный строитель) — конструирует и собирает вместе части продукта посредством реализации интерфейса Builder, предоставляет интерфейс для доступа к продукту; • Director (Распорядитель) — конструирует объект Product, пользуясь интерфейсом Builder; • Product (Продукт) — представляет сложный конструируемый объект. Задание 1. Реализовать иерархию классов-строителей, конструирующих продукты-строки. Иерархия включает абстрактный класс Builder, который предоставляет интерфейс для инициализации продукта (BuildStart) и создания трех его фрагментов (BuildPartA, BuildPartB, BuildPartC), и конкретные классы ConcreteBuilder1 и ConcreteBuilder2, которые определяют конкретные способы конструирования. Методы BuildStart, BuildPartA, BuildPartB, BuildPartC не имеют параметров и не возвращают значений. В абстрактном классе Builder подобные методы обычно не выполняют никаких действий, хотя и не являются абстрактными; это позволяет конкретному классу-строителю не переопределять некоторые из них, если его устраивает поведение по умолчанию. Кроме того, в классе Builder определен абстрактный метод GetResult без параметров, возвращающий строку-продукт. Конкретные классы ConcreteBuilder1 и ConcreteBuilder2 содержат строковое поле product; их метод GetResult возвращает текущее значение поля product. В конструкторе этих классов поле product инициализируется пустой строкой, это же действие выполняет и метод BuildStart. Каждый из методов BuildPartA, BuildPartB, BuildPartC добавляет к строке product новый текстовый фрагмент; для класса ConcreteBuilder1 фрагменты A, B, C представляют собой строки «-1-», «-2-» и «-3-», а для класса ConcreteBuilder2 — строки «=*=», «=**=» и «=***=». Также определить класс Director, содержащий поле b — ссылку на объект типа Builder (поле b инициализируется в конструкторе с использованием одноименного параметра) и два метода: Construct(templat) и GetResult. Метод GetResult не имеет параметров и возвращает значение метода GetResult объекта b, т. е. построенный продукт. Метод Construct обеспечивает построение продукта; его строковый параметр templat определяет план строительства. В данном случае план определяется последовательностью символов «A», «B», «C», каждый из которых соответствует фрагменту A, B, C, добавляемому к конструируемой строке в указанном порядке. Строка templat может содержать символы, отличные от «A», «B», «C»; подобные символы игнорируются. В начале своей работы метод Construct должен вызвать метод BuildStart. Тестирование разработанной системы классов. Даны пять строк, каждая из которых содержит план строительства. Создать два экземпляра d1 и d2 класса Director, передав первому экземпляру объект типа ConcreteBuilder1, а второму — объект типа ConcreteBuilder2. Вызвать метод Construct объектов d1 и d2 для каждой из исходных строк и вывести полученные результаты, используя метод GetResult (вначале выводятся результаты для первой исходной строки, затем для второй и т. д.).
[C++] class Builder
{
public:
virtual void BuildStart() {}
virtual void BuildPartA() {}
virtual void BuildPartB() {}
virtual void BuildPartC() {}
virtual string GetResult() = 0;
virtual ~Builder()
{
Show("Builder");
}
};
// Implement the ConcreteBuilder1
// and ConcreteBuilder2 descendant classes
class Director
{
shared_ptr<Builder> b;
public:
Director(shared_ptr<Builder> b);
string GetResult();
void Construct(string templat);
~Director()
{
Show("Director");
}
};
Director::Director(shared_ptr<Builder> b) : b(b) {}
string Director::GetResult()
{
return b->GetResult();
}
void Director::Construct(string templat)
{
// Implement the method
}
[C#] public abstract class Builder
{
public virtual void BuildStart() {}
public virtual void BuildPartA() {}
public virtual void BuildPartB() {}
public virtual void BuildPartC() {}
public abstract string GetResult();
}
// Implement the ConcreteBuilder1
// and ConcreteBuilder2 descendant classes
public class Director
{
Builder b;
public Director(Builder b)
{
this.b = b;
}
public string GetResult()
{
return b.GetResult();
}
public void Construct(string templat)
{
// Implement the method
}
}
[Java] abstract class Builder {
public void buildStart() {}
public void buildPartA() {}
public void buildPartB() {}
public void buildPartC() {}
public abstract String getResult();
}
// Implement the ConcreteBuilder1
// and ConcreteBuilder2 descendant classes
class Director {
private Builder b;
public Director(Builder b) {
this.b = b;
}
public String getResult() {
return b.getResult();
}
public void construct(String templat) {
// Implement the method
}
}
[Python] class ConcreteBuilder1:
def __init__(self):
pass
# Implement the "constructor"
def buildStart(self):
pass
# Implement the method
def buildPartA(self):
pass
# Implement the method
def buildPartB(self):
pass
# Implement the method
def buildPartC(self):
pass
# Implement the method
def getResult(self):
pass
# Implement the method
# Implement the ConcreteBuilder2 class
class Director:
def __init__(self, b):
self.__b = b
def getResult(self):
return self.__b.getResult()
def construct(self, templat):
pass
# Implement the method
[Ruby] class ConcreteBuilder1
def initialize
# Implement the "constructor"
end
def buildStart
# Implement the method
end
def buildPartA
# Implement the method
end
def buildPartB
# Implement the method
end
def buildPartC
# Implement the method
end
def getResult
# Implement the method
end
end
# Implement the ConcreteBuilder2 class
class Director
def initialize(b)
@b = b
end
def getResult
@b.getResult
end
def construct(templat)
# Implement the method
end
end
[Julia] abstract type Builder
end
# Implement the ConcreteBuilder1,
# ConcreteBuilder2 descendant structs,
# their constructors
# and the required functions
function getResult(b::Builder)
# Implement the function
end
struct Director
b::Builder
end
function getResult(d::Director)
getResult(d.b)
end
function construct(d::Director, templat::String)
# Implement the function
end
[PascalABC.NET] type
Builder = abstract class
public
procedure BuildStart; virtual;
begin
end;
procedure BuildPartA; virtual;
begin
end;
procedure BuildPartB; virtual;
begin
end;
procedure BuildPartC; virtual;
begin
end;
function GetResult: string; abstract;
end;
// Реализуйте классы-потомки ConcreteBuilder1 и ConcreteBuilder2
Director = class
private
b: Builder;
public
constructor Create(b: Builder);
begin
self.b := b;
end;
function GetResult: string;
begin
Result := b.GetResult;
end;
procedure Construct(templat: string);
begin
// Реализуйте метод
end;
end;
OOP1Creat10°. Builder (Строитель) — порождающий паттерн. Задание 2. Реализовать систему классов, позволяющую по строковым описаниям генерировать идентификаторы, которые удовлетворяют соглашениям различных языков программирования. Каждое строковое описание представляет собой одно или более слов, разделенных одним или несколькими пробелами; начальные и конечные пробелы отсутствуют, регистр букв является произвольным. Абстрактный класс Builder содержит методы для конструирования первого символа идентификатора (BuildStart), первого символа каждого последующего слова (BuildFirstChar), последующих символов слов (BuildNextChar) и символов-разделителей между словами (BuildDelim). Методы BuildStart, BuildFirstChar и BuildNextChar имеют символьный параметр, используемый при конструировании (возможно, после изменения его регистра); метод BuildDelim не имеет параметров. Все эти методы не возвращают значений; в классе Builder они не выполняют никаких действий. Кроме того, класс Builder содержит абстрактный метод GetResult без параметров, возвращающий строковое значение. Конкретные классы BuilderPascal, BuilderPython, BuilderC содержат строковое поле product, которое инициализируется пустой строкой в конструкторе, обновляется в методе BuildStart и дополняется в методах BuildFirstChar, BuildNextChar и, возможно, в методе BuildDelim. Метод GetResult возвращает значение поля product. При определении остальных методов конкретных строителей следует учитывать правила формирования идентификаторов для конкретных языков программирования: • для языка Pascal идентификатор должен начинаться со строчной (маленькой) буквы, последующие слова — с заглавной буквы, все прочие буквы являются строчными, символы-разделители не используются; • для языка Python все буквы являются строчными, а между словами добавляется символ подчеркивания; • для языка С все буквы являются строчными, символы-разделители не используются. Из перечисленных правил следует, в частности, что для классов BuilderPascal и BuilderC не требуется переопределять метод BuildDelim. Также определить класс Director, содержащий ссылочное поле b типа Builder (поле b инициализируется в конструкторе с использованием одноименного параметра), и два метода: Construct(templat) и GetResult. Метод GetResult не имеет параметров и возвращает значение метода GetResult объекта b. Метод Construct обеспечивает построение продукта; его строковый параметр templat содержит строковое описание, на основании которого должен конструироваться идентификатор по правилам, реализованным в строителе b. В начале работы метод Construct должен вызвать метод BuildStart с параметром — первым символом строки templat. При анализе строки templat необходимо различать первый пробел в последовательности пробелов (для которого надо вызывать метод BuildDelim) и последующие пробелы (которые должны игнорироваться). Тестирование разработанной системы классов. Даны пять строк, содержащих строковые описания, которые удовлетворяют условиям, перечисленным в начале формулировки задания. Создать три объекта-распорядителя Director, связанных со строителями BuilderPascal, BuilderPython, BuilderC, сохранив их в массиве или другой структуре данных. Используя методы Construct и GetResult каждого из созданных распорядителей, получить по каждой из исходных строк идентификаторы на языке Pascal, Python и C и вывести их в указанном порядке (вначале выводятся идентификаторы, полученные на основе первой строки, затем на основе второй и т. д.).
[C++] class Builder
{
public:
virtual void BuildStart(char c) {}
virtual void BuildFirstChar(char c) {}
virtual void BuildNextChar(char c) {}
virtual void BuildDelim() {}
virtual string GetResult() = 0;
virtual ~Builder()
{
Show("Builder");
}
};
// Implement the BuilderPascal, BuilderPyhton
// and BuilderC descendant classes
class Director
{
shared_ptr<Builder> b;
public:
Director(shared_ptr<Builder> b);
string GetResult();
void Construct(string templat);
~Director()
{
Show("Director");
}
};
Director::Director(shared_ptr<Builder> b) : b(b) {}
string Director::GetResult()
{
return b->GetResult();
}
void Director::Construct(string templat)
{
b->BuildStart(templat[0]);
// Complete the implementation of the method
}
[C#] public abstract class Builder
{
public virtual void BuildStart(char c) {}
public virtual void BuildFirstChar(char c) {}
public virtual void BuildNextChar(char c) {}
public virtual void BuildDelim() {}
public abstract string GetResult();
}
// Implement the BuilderPascal, BuilderPyhton
// and BuilderC descendant classes
public class Director
{
Builder b;
public Director(Builder b)
{
this.b = b;
}
public string GetResult()
{
return b.GetResult();
}
public void Construct(string templat)
{
b.BuildStart(templat[0]);
// Complete the implementation of the method
}
}
[Java] abstract class Builder {
public void buildStart(char c) {}
public void buildFirstChar(char c) {}
public void buildNextChar(char c) {}
public void buildDelim() {}
public abstract String getResult();
}
// Implement the BuilderPascal, BuilderPyhton
// and BuilderC descendant classes
class Director {
private Builder b;
public Director(Builder b) {
this.b = b;
}
public String getResult() {
return b.getResult();
}
public void construct(String templat) {
b.buildStart(templat.charAt(0));
// Complete the implementation of the method
}
}
[Python] class BuilderPascal:
def __init__(self):
pass
# Implement the "constructor"
def buildStart(self, c):
pass
# Implement the method
def buildFirstChar(self, c):
pass
# Implement the method
def buildNextChar(self, c):
pass
# Implement the method
def buildDelim(self):
pass
# Implement the method
def getResult(self):
pass
# Implement the method
# Implement the BuilderPyhton and BuilderC classes
class Director:
def __init__(self, b):
self.__b = b
def getResult(self):
return self.__b.getResult()
def construct(self, templat):
self.__b.buildStart(templat[0])
# Complete the implementation of the method
[Ruby] class BuilderPascal
def initialize
# Implement the "constructor"
end
def buildStart(c)
# Implement the method
end
def buildFirstChar(c)
# Implement the method
end
def buildNextChar(c)
# Implement the method
end
def buildDelim
# Implement the method
end
def getResult
# Implement the method
end
end
# Implement the BuilderPyhton and BuilderC classes
class Director
def initialize(b)
@b = b
end
def getResult
@b.getResult
end
def construct(templat)
@b.buildStart(templat[0])
# Complete the implementation of the method
end
end
[Julia] abstract type Builder
end
# Implement the BuilderPascal, BuilderPyhton,
# BuilderC descendant structs, their constructors
# and the required functions
function getResult(b::Builder)
# Implement the function
end
struct Director
b::Builder
end
function getResult(d::Director)
getResult(d.b)
end
function construct(d::Director, templat::String)
# Implement the function
end
[PascalABC.NET] type
Builder = abstract class
public
procedure BuildStart(c: Char); virtual;
begin
end;
procedure BuildFirstChar(c: Char); virtual;
begin
end;
procedure BuildNextChar(c: Char); virtual;
begin
end;
procedure BuildDelim; virtual;
begin
end;
function GetResult: string; abstract;
end;
// Реализуйте классы-потомки BuilderPascal,
// BuilderPyhton и BuilderC
Director = class
private
b: Builder;
public
constructor Create(b: Builder);
begin
self.b := b;
end;
function GetResult: string;
begin
Result := b.GetResult;
end;
procedure Construct(templat: string);
begin
b.BuildStart(templat[0]);
// Завершите реализацию метода
end;
end;
|