ООП для школьников.

 

========================================================================================================================

В этой теме будет несколько о-о-очень простых примеров использования ООП.

Приветствуются вопросы от начинающих программистов. Тех, кто действительно хочет разобраться в ООП.

"Умники", считающие, что я что-то делаю неправильно, заведите свои темы и там делайте правильно. Здесь вы ни кому не нужны.

Споры о нужности и ненужности ООП здесь также неуместны.

========================================================================================================================


1. Допустим мы что-то делаем с точками на координатной плоскости.

Пусть их будет всего 10.

Хранить их в памяти можно разными способами.

Так:

double x1, y1, x2, y2, x3, y3, x4, y4, x5, y5, x6, y6, x7, y7, x8, y8, x9, y9, x10, y10;

Или так:

double pointsX[10];
double pointsY[10];

Или так:

double points[10][2];

Но намного удобнее сделать так:

struct POINT
{
  double x;
  double y;
};

POINT points[10];

У нас появился новый тип данных, который представляет собой точку на плоскости.

Мы работаем с точкой как с отдельной сущностью.

Для примера напишем функцию, которая вычисляет расстояние между точками.

POINT p1, p2;

double D = Distance( p1, p2 );

double Distance( const POINT& p1, const POINT& p2 )
{
  double dx = p1.x - p2.x;
  double dy = p1.y - p2.y;
  return sqrt( dx * dx + dy * dy );
}

Таким образом, ООП даёт нам возможность программировать на языке задачи.


Продолжение следует...

 

2. Полиморфизм.

Допустим у нас есть такой код:

int iA = 1;
int iB = 3;

double dA = 4.5;
double dB = 3.14;

string sA = "abcd";
string sB = "efgh";

int iC = iA + iB;

double dC = dA + dB;

string sC = sA + sB;

В этих трёх случаях за плюсиком скрываются три разные функции сложения.

Это и называется полиморфизм.

ООП позволяет расширить это действие для других данных.

Например у нас есть класс matrix. Он находится в файле matrix.mqh

Тогда мы можем написать такой код:

#include <matrix.mqh>

matrix< double > mA( 2, 2 );
mA[0][0] = 1.0;
mA[0][1] = 2.0;
mA[1][0] = 3.0;
mA[1][1] = 4.0;

matrix< double > mB( 2, 2 );
mB[0][0] = 5.0;
mB[0][1] = 6.0;
mB[1][0] = 7.0;
mB[1][1] = 8.0;

matrix< double > mC = mA + mB;

Здесь ООП позволяет нам разделить ответственность между разными частями программы.

Когда мы пишем класс matrix, мы не думаем как он будет использоваться.

Когда же он написан и отлажен, мы используем его в разных задачах, уже не думая о правилах сложения и умножения матриц.

А просто ставим + и *.


Продолжение следует ...

 

3. Виртуальные функции.

Допустим мы написали несколько классов, представляющих собой геометрические фигуры.

Все они наследуются от одного базового класса Shape.

class Shape
{
public:
  Shape();

  virtual void Draw() = 0;
};

class Circle : public Shape
{
public:
  Circle();

  virtual void Draw();
};

class Rectangle : public Shape
{
public:
  Rectangle();

  virtual void Draw();
};

class Star : public Shape
{
public:
  Star();

  virtual void Draw();
};

Мы можем поместить все эти фигуры в один массив, если объявим массив указателей на базовый класс.

Shape* shapes[10];

В функции Init() мы заполняем этот массив.

void Init()
{
  for( int i = 0; i < 10; i++ )
  {
    switch( i % 3 ){
      case 0:
        shapes[i] = new Circle();
        break;
      case 1:
        shapes[i] = new Rectangle();
        break;
      case 2:
        shapes[i] = new Star();
        break;
    }
  }
}

Функция OnPaint() вызывается, когда нам нужно вывести все фигуры на экран.

void OnPaint()
{
   for( int i = 0; i < 10; i++ )
   {
      shapes[i].Draw();
   }
}

Мы просто перебираем в цикле все фигуры и вызываем для каждой функцию Draw().

Здесь для каждой фигуры вызывается своя функция, которая знает как рисовать эту конкретную фигуру.

В этом суть виртуальных функций.

И конечно в конце не забудем их удалить.

void OnDeinit()
{
  for( int i = 0; i < 10; i++ ){
    delete shapes[i];
  }
}


Продолжение следует ...

 

Не, они (школьник) не поймут. Тем более матрицы какие-то. Они скажут: нафига нам этот полиморфизм... какой-то? Тем более, что в эмкуле его эффективность проявляется только если вариантов этак... не менее 10-ти.

Начинать наверно надо с возможности объединения функций и переменных - с целью избавиться от глобальных переменных и связанной с ними путаницей.

 
Dmitry Fedoseev:

Не, они (школьник) не поймут. Тем более матрицы какие-то. Они скажут: нафига нам этот полиморфизм... какой-то? Тем более, что в эмкуле его эффективность проявляется только если вариантов этак... не менее 10-ти.

Начинать наверно надо с возможности объединения функций и переменных - с целью избавиться от глобальных переменных и связанной с ними путаницей.

Наверное )))

Я вот лично до сих пор не понимаю, нужен этот ООП или не нужен... Не вижу явных преимуществ (если не заниматься однотипными задачами, наверное). Да и не нашёл простого и ясного введения в эту тему (может потому, что не очень то и стремился?) )))))))

 

4. Инкапсуляция.

Здесь нередко можно услышать: "Зачем в ООП нужно делать зарытые члены класса? Хотим чтобы всё было открыто и доступно везде."

Но ООП не заставляет делать все члены закрытыми. Программист сам решает что нужно скрыть, а что нет.

И скрывают обычно то, что должно быть скрыто, чтобы уменьшить вероятность случайной порчи данных.

Например у нас есть кнопка, у которой можно устанавливать любой цвет.

class ColorButton
{
   color myColor;

public:
   ColorButton( color clr ) : myColor( clr ){}

   color GetColor() const
   {
      return myColor;
   }

   void SetColor( color clr )
   {
      myColor = clr;
      Update();
   }

   void Update();
};

Мы могли бы сделать переменную myColor открытой и в любой момент изменять цвет кнопки простым присваиванием.

Но присваивание этой переменной не вызовет немедленную перерисовку кнопки.

Поэтому мы делаем переменную myColor закрытой. А для изменения цвета вызываем функцию SetColor().

Эта функция, кроме присваивания переменной, сообщает системе, что кнопку нужно перерисовать.

В неё же можно поместить и любые другие необходимые действия.

Чтобы получить цвет кнопки есть функция GetColor(). Её вызов нисколько не дороже прямого обращения к переменной,

так как компилятор легко это может оптимизировать.

 
Koldun Zloy:

4. Инкапсуляция.


Я правильно понимаю, что в данном примере это и есть на сленге, те самые гетеры и сеторы ?

 
Roman:

Я правильно понимаю, что в данном примере это и есть на сленге, те самые гетеры и сеторы ?

Да. Всё правильно.

 
В эмкуле нет геттеров и сеттеров.
 
Dmitry Fedoseev:
В эмкуле нет геттеров и сеттеров.

Это определяет не MQL, а программист. Захочет - будут.

 
Ihor Herasko:

Это определяет не MQL, а программист. Захочет - будут.

Это определяет язык программирования.