|
Решение на языке Python
Теперь обсудим, как выполнять это же задание OOP1Creat1 на другом языке, тоже
имеющим возможность применения методов ООП, а именно на языке
Python. Как и для языка C++, мы будем использовать среду разработки Visual Studio Code.
На компьютере должен быть также установлен базовый вариант системы Python версии 3
и, кроме того, для среды Visual Studio Code должно быть установлено расширение Python (Microsoft).
Чтобы настроить среду Visual Studio Code для языка Python в качестве текущей среды
программирования электронного задачника, достаточно запустить программу PT4Load
и выполнить те же действия, что и для варианта этой среды для языка C++,
а именно вызвать контекстное меню и выбрать в нем пункт с названием Visual Studio Code (Python).
При этом даже не потребуется изменять имя задания, так как программа PT4Load помнит ранее введенное имя.
После нажатия кнопки Загрузка будет создана заготовка для языка Python,
которая сразу загрузится в среду Visual Studio Code.
Для языка Python, как и для языка C++, предусмотрены специальные заготовки
к заданиям по паттернам проектирования. Эти заготовки включают фрагменты описаний
необходимых классов, а также функцию solve, в которой надо выполнять
необходимые действия по вводу исходных данных, их обработке и выводу результатов.
from pt4 import *
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
def solve():
task("OOP1Creat1")
start(solve)
Анализируя созданную заготовку, можно обратить внимание на отсутствие
описаний абстрактных методов, а также классов, все методы которых являются
абстрактными (в частности, в заготовке отсутствует класс Product).
Это означает, что нам достаточно реализовать только те классы из системы классов,
описываемой в задании, которые должны содержать конкретные методы и поля.
Иногда иерархия классов, описанная в задании, сохранится; например,
из текста заготовки мы видим, что класс ConcreteCreator1 является потомком
класса Creator (класс-предок указывается в заголовке описания класса-потомка
в круглых скобках). Однако часто вместо иерархии классов мы будем получать
набор отдельных классов, не связанных отношением наследования
(в нашем случае такими классами будут ConcreteProduct1 и ConcreteProduct2).
Тем не менее, это не будет препятствовать совместному использованию объектов
таких классов (в частности, их включению в качестве элементов в некоторую
структуру данных). Главное, чтобы созданные классы имели «одинаковые» методы.
Подобный подход, при всей его гибкости, имеет очевидный недостаток:
невозможность выявить ошибки программы (прежде всего, различные опечатки)
на ранних этапах ее разработки, до непосредственного запуска.
Имена методов, использованные в заготовке, начинаются со строчных
(маленьких) букв, как это принято в языке Python.
Обратите внимание на завершающий оператор заготовки start(solve).
Он вызывает функцию start, описанную в модуле pt4 (директива импортирования
этого модуля указана в начале заготовки). Функция start инициализирует задачник,
вызывает функцию solve и обеспечивает необходимые действия по тестированию решения
и отображению на экране результатов работы программы.
Созданную заготовку можно сразу запустить на выполнение, чтобы ознакомиться
с формулировкой заданий и вариантами исходных и результирующих данных.
Для возможности запуска программы многие описанные в ней заготовки
методов снабжены командами-«заглушками» pass, которые можно удалить
при реализации методов (хотя делать это необязательно).
Приступим к решению задачи.
Поскольку абстрактный класс Product нам не требуется, достаточно
определить два «независимых» класса-продукта
ConcreteProduct1 и ConcreteProduct2; необходимо лишь, чтобы
они реализовывали одинаковый набор методов. Оба эти класса можно
породить непосредственно от базового класса object общего предка всех
классов в Python, причем имя этого класса-предка указывать необязательно:
class ConcreteProduct1:
def __init__(self, info):
self.__info = info.lower()
def getInfo(self):
return self.__info
def transform(self):
s = list(self.__info)
for i in range(len(s) - 2, -1, -1):
if s[i] != ' ':
s.insert(i+1, ' ')
self.__info = ''.join(s)
class ConcreteProduct2:
def __init__(self, info):
self.__info = info.upper()
def getInfo(self):
return self.__info
def transform(self):
s = list(self.__info)
for i in range(len(s) - 2, -1, -1):
if s[i] != '*':
s.insert(i+1, '**')
self.__info = ''.join(s)
Описания классов надо поместить перед функцией solve. Обратите
внимание на то, что имя конструктора является фиксированным (__init__),
а все необходимые поля класса должны явно определяться в конструкторе.
Все методы класса, включая конструктор, должны иметь особый первый
параметр, содержащий тот объект, для которого вызывается метод (или, в
случае конструктора, тот объект, который создается в конструкторе). Имя
этого параметра может быть произвольным, но традиционно применяется
имя self.
Обратите внимание на то, что имя конструктора является фиксированным (__init__),
а все необходимые поля класса должны явно определяться в конструкторе
(строго говоря, метод __init__ не является «настоящим» конструктором,
но именно в нем обычно выполняются все действия, связанные с созданием объекта).
Все методы класса, включая конструктор, должны иметь особый первый параметр,
содержащий тот объект, для которого вызывается метод (или, в случае конструктора,
тот объект, который создается в конструкторе). Имя этого параметра может быть
произвольным, но традиционно применяется имя self.
Кроме того, обратите внимание на использование двойного подчеркивания
в имени поля __info. С помощью двойного подчеркивания в языке Python
оформляются закрытые члены классов (хотя методы, имена которых начинаются
и заканчиваются двойным подчеркиванием, например, __init__, считаются
специальными и не являются закрытыми).
При реализации метода transform мы использовали тот же алгоритм, что и в случае языка C++,
однако, поскольку в языке Python отсутствует метод insert для строковых данных,
мы вначале преобразовали строковое поле __info в список (list), затем в цикле дополнили
список новыми элементами и в конце объединили элементы списка в итоговую строку __info методом join.
Приведение всех символов строки к нижнему или верхнему регистру в конструкторах __init__
в языке Python реализовать проще, чем в языке C++, так как стандартная библиотека
Python имеет соответствующие функции методы lower и upper класса str.
Иерархия классов-создателей в решении на языке Python сохранится,
так как в базовый класс Creator этой иерархии должен содержать
неабстрактный метод AnOperation. Класс Creator в заготовке полностью определен,
реализация его классов-потомков ConcreteCreator1 и ConcreteCreator2,
как и в случае языка C++, сложностей не представляет::
class ConcreteCreator1(Creator):
def factoryMethod(self, info):
return ConcreteProduct1(info)
class ConcreteCreator2(Creator):
def factoryMethod(self, info):
return ConcreteProduct2(info)
Еще раз подчеркнем, что успешная попытка запуска данной
программы (для этого достаточно нажать клавишу F5) еще не будет
свидетельствовать о том, что наша система классов разработана правильно
(или даже что в описаниях классов не содержится опечаток), поскольку
реальная проверка методов этих классов будет выполнена только при их
вызове в функции solve.
Нам осталось добавить в функцию solve операторы, обеспечивающие
ввод исходных данных, их преобразование и вывод результатов. Для ввода
данных любых скалярных типов (в том числе строк) в варианте задачника
для языка Python предусмотрена функция get(), а для вывода любого
количества результатов функция put с произвольным числом
параметров:
def solve():
task("OOP1Creat1")
c1 = ConcreteCreator1()
c2 = ConcreteCreator2()
for i in range(5):
s = get()
put(c1.anOperation(s), c2.anOperation(s))
После запуска полученной программы (и, возможно, исправления
обнаруженных опечаток) мы получим сообщение о том, что задание
выполнено.
|