Programming Taskbook


E-mail:

Пароль:

Регистрация пользователя   Восстановление пароля

 

ЮФУ

Электронный задачник по программированию

©  М. Э. Абрамян (Южный федеральный университет), 1998–2022

 

PT for OOP | Группы заданий | OOP1Creat

PrevNext


Порождающие паттерны

Factory Method, Abstract Factory

OOP1Creat1°.

OOPFactoryMethod.png

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
        return 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

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)
        return ConcreteProduct1.new(info)
    end

    def anOperation(info)
        p = factoryMethod(info)
        p.transform
        p.transform
        return p.getInfo
    end
end

# Implement the ConcreteCreator2 descendant class

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 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
        return "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
        return zoo
    end
end

class CatCreator < AnimalCreator
    def createAnimal(ind, name)
        # Implement the method
    end
end

# Implement the ApeCreator descendant class

OOP1Creat4°.

OOPAbstractFactory.png

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

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

Singleton, Prototype, Builder

OOP1Creat6°.

OOPSingleton.png

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 для объектов этого класса, поэтому в данном задании следует применять обычные указатели и операции newdelete.

[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
        return @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

OOP1Creat7°.

OOPPrototype.png

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

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

OOP1Creat9°.

OOPBuilder.png

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
        return @b.getResult
    end

    def construct(templat)
        # Implement the method
    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
        return @b.getResult
    end

    def construct(templat)
        @b.buildStart(templat[0])
        # Complete the implementation of the method
    end
end

PrevNext

 

Рейтинг@Mail.ru

Разработка сайта:
М. Э. Абрамян, В. Н. Брагилевский

Последнее обновление:
19.04.2022