Реализация концепций «наследования» и «полиморфизма» в объектно-ориентированном программировании на примере создания двух классов. Интерфейсы ICloneable, IEquatable


Реализация концепций «наследования» и «полиморфизма» в объектно-ориентированном программировании на примере создания двух классов. Интерфейсы ICloneable, IEquatable

Содержание



Условие задачи

В разработанных классах необходимо реализовать такие концепции ООП как «наследование» и «полиморфизм«.

Нужно описать базовый класс (класс-предок) и унаследовать (наследовать) от него. Часть функциональности классов нужно реализовать с использованием свойств.

Каждый класс должен содержать реализацию по крайней мере:

  • двух конструкторов;
  • двух методов;
  • двух свойств;
  • двух интерфейсов.

Необходимо переопределить операции над объектами классов. Создать класс, который определяет окружность на плоскости. Используя разработанные классы найти площадь круга.

Реализовать интерфейсы ICloneable, IEquatable.


Математическая постановка задачи

На рисунке 1 изображена окружность на плоскости.

окружность плоскость рисунок

Рис. 1. Изображение окружности на плоскости

Как видно из рисунка 1, для обозначения окружности на плоскости нужно иметь:

  • координаты (x, y) точки середины окружности;
  • радиус окружности R.

Площадь окружности исчисляется по формуле:

Площадь окружности Формулагде S — площадь окружности; R — радиус окружности; π– константа, которая равняется 3.1415.

 


Выполнение

1. Создание нового проекта.

Запустить Microsoft Visual Studio. Создать новый проект как Windows Forms Application. Пример создания нового проекта приведен здесь.

Автоматически будет создана новая форма приложения.

 


2. Размещение элементов управления на форме.

Разместить на форме следующие элементы управления (рисунок 2):

  • два элемента управления типа Label. Автоматически будут созданы объекты с именами label1 и label2;
  • элемент управления типа TextBox. Создается новый объект с именем textBox1;
  • элемент управления типа Button. Создается объект с именем button1.

C# Windows Forms Размещение элемент управления форма

Рис. 2. Размещение элементов управления на форме

 


3. Настройка свойств элементов управления.

Настроить следующие свойства элементов управления (рисунок 3):

  • в элементе управления label1 свойство Text = «R = «;
  • в элементе управления label2 свойство Text = «S = «;
  • в элементе управления button1 свойство Text = «Вычислить«;
  • в элементе управления Form1 свойство Text = «Площадь круга«.

C# Windows Forms Форма приложения

Рис. 3. Форма приложения после настройки элементов управления

После настройки формы нужно заняться созданием классов.

 


4. Классы, необходимые для решения задачи.

В соответствии с условием задачи, нужно создать два класса:

  • класс Point, отображающий точку на плоскости. Этот класс должен быть базовым;
  • класс Circle, отображающий круг. Этот класс должен унаследовать данные и методы класса Point (в соответствии с условием задачи).

В данной задаче класс Point называется базовым, а класс Circle – производным.

Итак, возникает иерархия с 2-х классов. Схема иерархии классов и отображение данных (полей) в классах изображенная на рисунке 4.

C# Иерархия классов рисунок

Рис. 4. Иерархия классов в соответствии с условием задачи

По ходу решения задачи в классы будут вноситься изменения: свойства и методы.

Опишем класс Point, содержащий только данные. На языке C# листинг класса Point будет следующий:

class Point
{
    // поля класса объявлены как protected
    protected float x; // координата x точки
    protected float y; // координата y точки
}

В классе Point данные класса описываются как protected. Это означает, что эти данные есть видимыми в производных классах и невидимыми извне (скрытые для доступа через объект класса).

Для доступа к данным будут использоваться свойства и специальные методы.

Таким же образом создается класс Circle. Пока что листинг класса Circle следующий:

class Circle:Point // наследует данные из класса Point
{
    // поля класса
    private float R; // радиус окружности
}

В описании класса Circle, начало строки

class Circle:Point

означает, что класс Circle наследует данные и методы класса Point.

Таким образом, на данный момент, в классе Circle есть доступными три поля:

  • поле x из унаследованного класса Point;
  • поле y из унаследованного класса Point;
  • поле R, которое есть внутренним полем класса Circle.

 


5. Реализация конструкторов класса Point.

В соответствии с условием задачи, каждый из классов должен содержать минимум два конструктора.

Последовательно в текст класса Point нужно добавить два конструктора. Первый конструктор без параметров инициализирует данные нулевыми значениями.

Второй конструктор – параметризованный. Этот конструктор получает входящими 2 параметра и устанавливает новые значения для внутренних переменных x и y.

Ниже приведен текст программного кода обоих конструкторов:

// конструктор 1 - без параметров
public Point()
{
    x = 0f; // обнуление значений координат
    y = 0f;
}

// конструктор 2 - с двумя параметрами
public Point(float nx, float ny)
{
    x = nx;
    y = ny;
}

Следует обратить внимание, что конструкторы имеют тип доступа public, так как они вызываются извне при создании объекта класса.

 


6. Реализация конструкторов класса Circle.

Таким же образом нужно добавить два конструктора в класс Circle.

// конструктор 1 - инициализирует поля нулевыми значениями
public Circle()
{
    x = 0f; // 0f - означает, что это значение имеет тип float
    y = 0f;
    R = 0f;
}

// конструктор 2 - с тремя параметрами
public Circle(float nx, float ny, float nR)
{
    x = nx;
    y = ny;
    R = nR;
}

 


7. Реализация методов класса Point.

В соответствии с условием задачи, в каждом классе должны быть реализованы два метода. Реализуем методы, которые выполняют следующие действия:

  • метод GetPoint(), возвращающий точку типа Point;
  • метод SetXY(), устанавливающий новые значения внутренних переменных класса Point.

Тип доступа для методов выбран как public.

Листинг методов следующий.

public Point GetPoint()
{
    Point p; // описывается переменная (объект), для которой память еще не выделена
    p = new Point(x,y); // выделяется память, создается новый экземпляр класса
    return p;
}

// метод, который устанавливает новые значения внутренних переменных
public void SetXY(float nx, float ny)
{
    x = nx;
    y = ny;
}

 


8. Реализация методов класса Circle.

Подобным образом создаются методы класса Circle. В классе Circle реализуются два метода. Первый метод GetCircle() возвращает объект (с выделением памяти) типа Circle.

Второй метод SetXYR() устанавливает новые значения внутренних переменных. Этот метод получает входными 3 параметра (координаты x, y и радиус R окружности). В методе продемонстрировано использование ключевого слова this для доступа к полям класса. В этом случае имена локальных переменных-параметров совпадают с именами полей класса и скрывают их видимость внутри метода.

Листинг методов следующий:

// методы класса Circle в соответствии с условием задачи
// метод, который получает новый экземпляр класса типа Circle
public Circle GetCircle()
{
    Circle c;
    c = new Circle(x, y, R);
    return c;
}

// метод, который устанавливает новые значения во внутренние поля класса Circle

public void SetXYR(float x, float y, float R)
{
    // пример использования ключевого слова this
    // во внутреннюю переменную x класса заносится значение локальной переменной x метода SetXYR()
    this.x = x;
    // так же заполняются внутренние переменные y и R класса Circle
    this.y = y;
    this.R = R;
}

 


9. Примеры использования конструкторов и методов классов Point и Circle.

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

Демонстрация методов GetPoint() и SetXY():

// класс Point
// вызов конструктора без параметров класса Point()
Point p = new Point();
Point p1 = null, p2 = null;
p1 = new Point(4.5f, 3.2f); // вызов конструктора с двумя параметрами
p2 = p1.GetPoint();
p2.SetXY(3.3f, -2.8f); // установить новые значения

// класс Circle
Circle c = new Circle(); // конструктор без параметров
Circle c1, c2;
c1 = new Circle(3.3f, -2.8f, 12f); // выделение памяти
c2 = c1.GetCircle(); // метод GetCircle()
c2.SetXYR(3.8f, -1.9f, 8f); // метод SetXYR()

 


10. Исходный код классов.

На данный момент классы Point и Circle имеют следующий вид:

// описание класса Point
class Point
{
    // поля класса объявлены как protected
    protected float x; // координата x точки
    protected float y; // координата y точки

    // конструктор 1 - без параметров
    public Point()
    {
        x = 0f;
        y = 0f;
    }

    // конструктор 2 - с двумя параметрами
    public Point(float nx, float ny)
    {
        x = nx;
        y = ny;
    }

    public Point GetPoint()
    {
        Point p; // описывается переменная (объект), для которой память еще не выделена
        p = new Point(x,y); // выделяется память, создается новый экземпляр класса
        return p;
    }

    // метод, который устанавливает новые значения внутренних переменных
    public void SetXY(float nx, float ny)
    {
        x = nx;
        y = ny;
    }
}

// описание класса Circle
class Circle:Point // наследует данные класса Point
{
    // поля класса
    private float R; // радиус окружности
    // конструктор 1 - инициализирует поля нулевыми значениями
    public Circle()
    {
        x = 0f;
        y = 0f;
        R = 0f;
    }

    // конструктор 2 - с тремя параметрами
    public Circle(float nx, float ny, float nR)
    {
        x = nx;
        y = ny;
        R = nR;
    }

    // методы класса Circle в соответствии с условием задачи
    // метод, который получает новый экземпляр класса типа Circle
    public Circle GetCircle()
    {
        Circle c; // создается новый объект типа класс Circle
        c = new Circle(x, y, R); // выделяется память для объекта c, создается экземпляр класса
        return c;
    }

    // метод, который устанавливает новые значения во внутренние поля класса Circle
    public void SetXYR(float x, float y, float R)
    {
        // пример использования ключевого слова this
        this.x = x; // во внутреннюю переменную x класса заносится значение локальной переменной x метода SetXYR()
        this.y = y; // так же заполняются внутренние переменные y и R класса Circle
        this.R = R;
    }
}

 


11. Реализация свойств класса Point.

Следующим шагом нужно создать два свойства класса Point.

Первое свойство возвращает или устанавливает значение координат точки x. Второе свойство возвращает/устанавливает значение координаты y.

Имя первого свойства XX. Имя второго свойства YY.

Листинг свойств XX и YY следующий:

// свойство, которое возвращает/устанавливает координату x
public float XX
{
    get { return x; }
    set { x = value; }
}

// свойство, которое возвращает/устанавливает координату y
public float YY
{
    get { return y; }
    set { y = value; }
}

После такого описания свойства XX и YY можно использовать. Далее приведен пример использования свойств XX и YY.

...

Point p = new Point(5.2f, 6.35f); // создание объекта типа Point
float x,y;

x = p.XX; // x = 5.2
y = p.YY; // y = 6.35

// переопределить значения
p.XX = 2.33f;
p.YY = -3.8f;

x = p.XX; // x = 2.33
y = p.YY; // y = -3.8

...

 


12. Реализация свойств класса Circle.

Аналогичным образом описываются свойства класса Circle. Первое свойство читает/устанавливает значение радиуса R. Второе свойство читает/устанавливает значение точки, которая есть центром кола (класс Point).

// свойства класса Circle
// свойство, которое возвращает значение радиуса окружности R
public float RR
{
    get { return R; }
    set { R = value; }
}

// свойство, которое возвращает значение точки типа Point, которая есть центром окружности
public Point PP
{
    get
    {
        Point p;
        p = new Point(x, y);
        return p;
    }

    set
    {
        x = value.XX;
        y = value.YY;
    }
}

В нижеследующем листинге описывается пример использования свойств PP и RR.

// создание объекта, в котором значение полей x = 5.5; y = -2.3; R = 12.8
Circle c = new Circle(5.5f, -2.3f, 12.8f);
float x, y, R;

// доступ к свойству PP класса Circle
x = c.PP.XX; // x = 5.5
y = c.PP.YY; // y = -2.3

// доступ к свойству RR
R = c.RR; // R = 12.8
c.RR = -20.85f;

 


13. Реализация методов интерфейса ICloneable в классах.

Если реализовать интерфейс ICloneable, то можно обеспечить глубокое копирование объектов.

В нижеследующем примере осуществляется поверхностное копирование объектов:

Point p1 = new Point(5.3f, -6.8f);
Point p2;

// поверхностное копирование
p2 = p1; // оба объекта указывают на один участок памяти

Интерфейс ICloneable представляет один метод для реализации. Этот метод называется Clone(). Этот метод нужно реализовать в классах Point и Circle. Реализация метода работает по одинаковому принципу.

Прежде всего нужно изменить заголовок класса Point. Вместо текста

class Point

нужно набрать

class Point:ICloneable

Это означает, что в классе Point нужно реализовать единственный метод Clone(). Листинг метода следующий:

// реализация метода Clone() в интерфейсе ICloneable
public object Clone()
{
    Point p = new Point(x, y);
    return p;
}

Так как класс Circle наследует класс Point, который наследует интерфейс ICloneable, то в классе Circle также можно реализовать метод Clone() (но не обязательно).

// Реализация интерфейса ICloneable в классе Circle
public object Clone()
{
    Circle c = new Circle();
    return c;
}

Пример использования метода Clone().

Point p1 = new Point(5.2f, 6.35f); // создание объекта
Point p2;

// глубокое копирование, так как реализован метод Clone()
p2 = (Point)p1.Clone();// для p2 отдельно выделяется память

// класс Circle
Circle c1 = new Circle(3.2f, -8.3f, 11f);
Circle c2 = null;
c2 = (Circle)c1.Clone(); // для c2 отдельно выделяется оперативная память

 


14. Реализация методов интерфейса IEquatable.

Интерфейс IEquatable реализуется в тех классах, где нужно определить порядок сравнения двух объектов на равенство их значений. В этом интерфейсе определяется только один метод Equals(). Этот метод возвращает значение true, если значение вызывающего объекта есть равным значению другого объекта, который является параметром в методе Equals().

Для класса Point реализуем метод Equals(), который сравнивает на равенство значения переменных x и y.

Листинг метода Equals() для класса Point следующий:

// реализация метода Equals() из интерфейса IEquatable
public bool Equals(Point p2)
{
    if ((this.x == p2.x) && (this.y == p2.y))
        return true;
    else
        return false;
}

Листинг метода Equals() для класса Circle следующий:

// Реализация метода Equals() из интерфейса IEquatable
public bool Equals(Circle c2)
{
    if ((this.x == c2.x) && (this.y == c2.y) && (this.R == c2.R))
        return true;
    else
        return false;
}

В программе метод Equals() для классов можно использовать следующим образом:

// демонстрация метода Equals() для класса Point
Point p1 = new Point(3f, -5.5f);
Point p2;
bool f;

p2 = (Point)p1.Clone();
f = p1.Equals(p2); // f = true

// демонстрация метода Equals() для класса Circle
Circle c1 = new Circle(3.2f, -8.3f, 11f);
Circle c2 = new Circle(3.2f, -8.3f, 11f);
f = c1.Equals(c2); // f = true

 


15. Реализация метода вычисления площади круга в классе Circle.

Название метода – GetSquare(). Для вычисления площади круга нужно иметь только радиус R. Радиус получается с внутренней переменной R. Результат вычисления есть тип float.

В тексте класса Circle нужно добавить следующий метод:

// Метод, который вычисляет площадь круга
public float GetSquare()
{
    const float pi = 3.1415f;
    float s;
    s = pi * R * R;
    return s;
}

 


16. Сокращенный листинг класса Point.

Итак, создание классов завершено.

Сокращенный листинг класса Point следующий (детали реализации описываются в предыдущих пунктах):

class Point:ICloneable,IEquatable<Point>
{
    // поля класса объявленные как protected
    protected float x; // координата x точки
    protected float y; // координата y точки

    // конструктор 1 - без параметров
    public Point() { ... }

    // конструктор 2 - с двумя параметрами
    public Point(float nx, float ny) { ... }

    // метод, который возвращает точку с координатами
    public Point GetPoint() { ... }

    // метод, который устанавливает новые значения внутренних переменных
    public void SetXY(float nx, float ny) { ... }

    // свойство, которое возвращает/устанавливает координату x
    public float XX
    {
        get { ... }
        set { ... }
    }

    // свойство, которое возвращает/устанавливает координату y
    public float YY
    {
        get { ... }
        set { ... }
    }

    // реализация метода Clone() в интерфейсе ICloneable
    public object Clone() { ... }

    // реализация метода Equals() из интерфейса IEquatable
    public bool Equals(Point p2) { ... }
}

 


17. Сокращенный листинг класса Circle.

Низшее приведен листинг класса Circle, в котором скрыты детали реализации. Реализация методов, свойств, конструкторов описывается в предыдущих пунктах.

class Circle:Point // наследует данные класса Point
{
    // поля класса
    private float R; // радиус окружности

    // конструктор 1 - инициализирует поля нулевыми значениями
    public Circle() { ... }

    // конструктор 2 - с тремя параметрами
    public Circle(float nx, float ny, float nR) { ... }

    // методы класса Circle в соответствии с условием задачи
    // метод, который получает новый экземпляр класса типа Circle
    public Circle GetCircle() { ... }

    // метод, который устанавливает новые значения во внутренние поля класса Circle
    public void SetXYR(float x, float y, float R) { ... }

    // свойства класса Circle
    // свойство, которое возвращает значение радиуса окружности R
    public float RR
    {
        get { ... }
        set { ... }
    }

    // свойство, которое возвращает значение точки типа Point, которая есть центром окружности
    public Point PP
    {
        get { ... }
        set { ... }
    }

    // Реализация интерфейса ICloneable
    public object Clone() { ... }

    // Реализация метода Equals() из интерфейса IEquatable
    public bool Equals(Circle c2) { ... }

    // Метод, который вычисляет площадь круга
    public float GetSquare() { ... }
}

 


18. Программирование обработчика события клика на кнопке «Вычислить«.

Вычисление площади круга осуществляется в момент, когда пользователь сделает клик на кнопке «Вычислить«. Листинг обработчика события клика на кнопке «Вычислить» следующий:

private void button1_Click(object sender, EventArgs e)
{
    Circle c = new Circle();
    float s;

    // заполнить значения R
    c.RR = (float)Double.Parse(textBox1.Text);

    // вызвать метод определения площади круга
    s = c.GetSquare();
    label2.Text = "S = " + s.ToString();
}

Результат работы программы изображен на рисунке 5.

C# Windows Forms Выполнение программы

Рис. 5. Выполнение программы

 


Похожие темы