Programming Taskbook


E-mail:

Пароль:

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

 

ЮФУ SMBU

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

©  М. Э. Абрамян (Южный федеральный университет, Университет МГУ-ППИ в Шэньчжэне), 1998–2025

 

PT for OOP | Выполнение задания OOP1Creat1 | Решение на языках C# и PascalABC.NET

PrevNext


Решения на языках C# и PascalABC.NET

Теперь приведем решение задачи OOP1Creat1 на языке C#. Несмотря на то, что в нем, как и в решении для языка C++, потребуется реализовать систему классов в полном объеме, включая все абстрактные классы и методы, решение получится достаточно коротким, благодаря компактности конструкций самого языка и обширному набору функций из стандартной библиотеки платформы .NET. В завершающей части данного пункта мы приведем решение на языке PascalABC.NET, который имеет много общего с языком C# и также позволяет использовать библиотеку платформы .NET.

При решении задачи на языке C# будем по-прежнему использовать среду Visual Studio Code (задания на этом языке можно также выполнять в средах Visual Studio и SharpDevelop).

Начнем с обсуждения заготовки, созданной для этого задания. Ниже приводится текст этой заготовки, за исключением начальных директив using для подключения пространств имен, а также директивы namespace определения пространства имен для класса MyTask:

[C#]

public class MyTask : PT
{
    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

    public static void Solve()
    {
        Task("OOP1Creat1");

    }
}

В заготовках для языка C# все требуемые классы надо описывать в виде внутренних классов класса MyTask, который является потомком класса PT и содержит все элементы решения задачи. В частности, функция Solve тоже является методом класса MyTask. В остальном созданная заготовка очень похожа на заготовку для языка C++. Отметим лишь, что в языке C# все методы класса описываются непосредственно внутри описания класса и что после описаний классов не надо указывать точку с запятой. Некоторые отличия имеются также в правилах указания модификаторов доступа. Для языка C# модификатор override при переопределении виртуальных методов является обязательным, а в абстрактных методах вместо модификатора virtual используется модификатор abstract (а конструкция «= 0» не указывается).

Приведем вариант реализации конкретных классов-продуктов ConcreteProduct1 и ConcreteProduct2:

[C#]

public class ConcreteProduct1 : Product
{
    string info;
    public override string GetInfo()
    {
        return info;
    }
    public ConcreteProduct1(string info)
    {
        this.info = info.ToLower();
    }
    public override void Transform()
    {
        StringBuilder s = new StringBuilder(info);
        for (int i = s.Length - 2; i >= 0; i--)
            if (s[i] != ' ')
                s.Insert(i + 1, " ");
        info = s.ToString();
    }
}

public class ConcreteProduct2 : Product
{
    string info;
    public override string GetInfo()
    {
        return info;
    }
    public ConcreteProduct2(string info)
    {
        this.info = info.ToUpper();
    }
    public override void Transform()
    {
        StringBuilder s = new StringBuilder(info);
        for (int i = s.Length - 2; i >= 0; i--)
            if (s[i] != '*')
                s.Insert(i + 1, "**");
        info = s.ToString();
    }
}

Для того чтобы обеспечить более эффективное преобразование поля info в методе Transform, мы используем класс StringBuilder, методы которого (в отличие от аналогичных методов класса string) преобразуют исходный объект StringBuilder, а не создают новые строковые объекты.

Реализация конкретных классов-создателей ConcreteCreator1 и ConcreteCreator2, как и для предыдущих языков, сложностей не представляет:

[C#]

public class ConcreteCreator1 : Creator
{
    protected override Product FactoryMethod(string info)
    {
        return new ConcreteProduct1(info);
    }
}

public class ConcreteCreator2 : Creator
{
    protected override Product FactoryMethod(string info)
    {
        return new ConcreteProduct2(info);
    }
}

Организация ввода и вывода данных для языка C# похожа на вариант для языка Python, однако, чтобы обеспечить строгую типизацию, для ввода данных каждого типа необходимо использовать соответствующую функцию: GetInt(), GetDouble, GetString() и т. д. Вывод выполняется функцией Put с произвольным числом параметров. Используя эти средства ввода-вывода, получаем следующий вариант функции Solve:

[C#]

public static void Solve()
{
    Task("OOP1Creat1");
    var c1 = new ConcreteCreator1();
    var c2 = new ConcreteCreator2();
    for (int i = 0; i < 5; i++)
    {
        string s = GetString();
        Put(c1.AnOperation(s), c2.AnOperation(s));
    }
}

От варианта для языка C++ данный фрагмент отличается также другим синтаксисом вызова конструктора, в котором используется ключевое слово new (как при создании в C++ объекта в динамической памяти). Такой синтаксис подчеркивает важную особенность объектной модели языка C#, в которой все классы являются ссылочными типами и их объекты всегда размещаются в динамической памяти.

Очень важным является то обстоятельство, что программист не должен заботиться об освобождении памяти, выделенной для хранения объектов, поскольку за это действие отвечает специальная подсистема платформы .NET — сборщик мусора (garbage collector, GC). Наличие сборщика мусора значительно упрощает разработку программ. Напомним, что в языке C++ тоже имеются средства для автоматического освобождения памяти — умные указатели, однако при их неправильном использовании в программе всё равно могут происходить утечки памяти. В языке C# и многих других современных языках (в том числе Python, Ruby и Java, которые рассматриваются в данном разделе), освобождение памяти выполняется полностью автоматически. Платой за подобную автоматизацию является некоторое (хотя и очень незначительное) замедление работы программы во время активизации сборщика мусора и выполнения им необходимых действий по освобождению памяти.

При описании объектов-создателей c1 и c2 мы использовали ключевое слово var, позволяющее не указывать тип описываемой переменной, если его можно вывести из инициализирующего выражения. Заметим, что это же слово var можно было использовать и при описании переменных i и s.

Запустив полученный вариант решения, мы увидим окно задачника с сообщением о том, что задание выполнено.

Теперь обратимся к языку PascalABC.NET. Версия 1.1 задачника PT for OOP интегрирована в среду PascalABC.NET и позволяет создавать развернутые заготовки для всех заданий. Поскольку система PascalABC.NET активно используется в России при обучении школьников, реализованы два варианта заготовок, в которых комментарии выводятся как на русском, так и на английском языке (выбор варианта заготовки зависит от текущих языковых настроек среды PascalABC.NET).

Приведем заготовку, в которой используется русские комментарии.

[PascalABC.NET]

uses PT4;

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

begin
  Task('OOP1Creat1');

end.

Созданную заготовку можно запустить на выполнение, чтобы ознакомиться с формулировкой задачи и примером исходных данных и результатов. Напомним, что для запуска программы в среде PascalABC.NET достаточно нажать клавишу F9.

Как и для языка C#, заготовка для языка PascalABC.NET содержит полные описания абстрактных классов Product и Creator, причем в классе Creator уже реализован метод AnOperation, который не требуется изменять. Отличия от языка C# проявляются лишь в деталях синтаксиса; в частности, по-другому члены класса с одной и той же областью видимости оформляются в виде группы, перед которой указывается соответствующий модификатор (private, protected, internal, public). Модификатор abstract указывается после заголовка абстрактного метода; как и для языка C#, все абстрактные методы считаются виртуальными, причем модификатор virtual указывать необязательно (но при переопределении абстрактных методов необходимо указывать модификатор override).

При описании класса указываются его поля и заголовки методов, причем можно либо сразу приводить реализацию методов (как в языке C#), либо описывать методы после описании класса, снабжая при этом имена методов именем класса, например, Creator.AnOperation. В заготовках реализация методов всегда приводится непосредственно при описании класса.

Следуя этому варианту реализации методов, приведем описания конкретных классов-продуктов (эти описания должны располагаться в том же разделе type, что и описание класса Product):

[PascalABC.NET]

  ConcreteProduct1 = class(Product)
  private
    info: string;
  public
    constructor Create(info: string);
    begin
        self.info := info.ToLower();
    end;
    function GetInfo: string; override;
    begin
      Result := info;
    end;
    procedure Transform; override;
    begin
      for var i := info.Length - 1 downto 1 do
        if info[i] <> ' ' then
          Insert(' ', info, i + 1);
    end;
  end;

  ConcreteProduct2 = class(Product)
  private
    info: string;
  public
    constructor Create(info: string);
    begin
        self.info := info.ToUpper();
    end;
    function GetInfo: string; override;
    begin
      Result := info;
    end;
    procedure Transform; override;
    begin
      for var i := info.Length - 1 downto 1 do
            if info[i] <> '*' then
                Insert('**', info, i + 1);
    end;
  end;

Следует обратить внимание на то, что при описании класса-потомка имя класса-предок указывается в круглых скобках, а описания конструкторов начинаются со специального слова constructor, причем в качестве имени используется имя Create. Возвращаемое значение функции надо присваивать особой переменной Result, которую не нужно описывать. В данной реализации метода Transtorm мы не использовали класс StringBuilder, поскольку строки в PascalABC.NET являются изменяемыми, а одной из функций, позволяющей изменить строку, является функция Insert. При работе со строками в PascalABC.NET необходимо учитывать, что они индексируются от 1, и этот же способ индексирования используется в ряде стандартных функций, в частности, в использованной нами функции Insert.

Теперь приведем реализации конкретных классов-создателей (их следует указать после реализации абстрактного класса Creator):

[PascalABC.NET]

  ConcreteCreator1 = class(Creator)
  protected
    function FactoryMethod(info: string): Product; override;
    begin
      Result := new ConcreteProduct1(info);
    end;
  end;

  ConcreteCreator2 = class(Creator)
  protected
    function FactoryMethod(info: string): Product; override;
    begin
      Result := new ConcreteProduct2(info);
    end;
  end;

Здесь следует обратить внимание на то, что вызов конструкторов в языке PascalABC.NET может выполняться по тем же правилам, что и в языке C# (с использованием слова new), несмотря на то что при описании конструкторов используется слово Create.

Запустив полученный вариант программы, мы можем убедиться, что описания новых классов не содержат синтаксических ошибок. Осталось проверить правильность их реализации, обработав требуемым образом исходные данные. Эти действия надо выполнить в основном блоке программы после вызова функции Task (который уже содержится в заготовке):

[PascalABC.NET]

begin
  Task('OOP1Creat1');
  var c1 := new ConcreteCreator1;
  var c2 := new ConcreteCreator2;
  loop 5 do
  begin
    var s := ReadString;
    Print(c1.AnOperation(s), c2.AnOperation(s));
  end;
end.

Для ввода исходных данных с применением задачника предусмотрен набор функций (ReadInteger, ReadReal, ReadChar, ReadString и др.), а для вывода результатов — функция Print, в которой можно указывать произвольное число параметров. Обратите внимание на использование цикла loop, который является упрощенным вариантом цикла for и используется в случае, когда требуется выполнить требуемое число одинаковых действий.

Запустив данный вариант программы, мы получим сообщение о том, что задание выполнено.


PrevNext

 

Рейтинг@Mail.ru

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

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