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

ООП или что же все-таки это такое - объекто-ориентированное программирование

Привет всем!

Я хотел бы попробовать доступно и наглядно объяснить, что же все-таки такое объектно-ориентированное программирование (ООП), и зачем оно вообще нужно.

1. Вступление

А для начала немного истории.

На заре эпохи программирования, когда компьютеры еще были очень большие и "неповоротливые", программы писали напрямую в машинных кодах. Далее, чтобы облегчить жизнь программистам, в 1950 году был разработан первый мнемонический язык - "ассемблер". Но это был все еще низкоуровневый язык программирования, который напрямую использовал команды процессора и напрямую работал с памятью. И хотя этот язык очень нужный и полезный, и используется и по сей день, но, в силу его специфики и трудоемкости, сфера его применения весьма ограниченна. И какую-нибудь более менее серьезную программку на нем уже не напишешь.

Далее, чтобы сделать программирование более доступным и востребованным, были разработаны такие языки программирования как "Фортран", "Алгол", "Кобол" и др. Но я думаю, хотя со мной может быть и поспорят, что настоящая эпоха программистов началась после того, как в начале семидесятых годов прошлого века Деннис Ритчи из лаборатории Белла, более известной как Bell Labs, разработал язык С (Си), который в дальнейшем стал одним из самых популярных языков программирования. И именно он стал прародителем многих современных и популярных языков программирования, по крайней мере структура и синтаксис многих из них, например таких как C#, Java, Python, PHP очень напоминает именно язык Си, и такие языки даже иногда называют Си-подобные.

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

Но со временем приложения становились все более сложные, количество кода для написания приложений соответственно возрастало, увеличивалась и сложность написания этого кода и обслуживания. Росло и количество ошибок, которые необходимо было исправлять, тратя на это много драгоценного времени. Писать приложения становилось все более сложно и трудозатратно.

В восьмидесятых годах Бьёрн Страуструп, из той же лаборатории Белла, приступил к усовершенствованию языка Си, в том числе добавляя ему дополнительные возможности объектно-ориентированного программирования. Получившийся язык уже несколько отличался от классической версии языка Си, поэтому доработанный язык программирования Си переименовали в С++ (Си плюс-плюс). Первый выпуск нового языка программирования вышел в свет в восемьдесят пятом году, и этот язык уже содержал все возможности классического объектно-ориентированного программирования. А какую же пользу это принесло программистам?

Понятно, что сложную программу проще всего разбить на модули, которые потом уже можно было бы разрабатывать независимо друг от друга. И далее, используя эти модули как "черные ящики", реализовывать алгоритмы с помощью них, особо даже и не задумываясь, как они изнутри работают. Ну так вот, в процедурном программировании роль "черных ящиков" выполняют функции и процедуры, что является довольно маленькой единицей в масштабах полноценного приложения. В ООП же уже присутствует понятие класса, который включает в себя множество переменных, свойств и методов. Класс, это вполне самодостаточная единица (правда, далеко не всегда), способная выполнять целый ряд задач. Например, можно создать класс, с помощью которого можно обрабатывать изображения. Допустим, с помощью такого класса можно производить и разную конвертацию изображения, и изменения размеров изображения, и наложение фильтров, и еще множество другого. И все это можно делать, используя один единственный класс, абсолютно не задумываясь, как это все работает изнутри, а все что требуется знать программисту, так это какие методы реализованы в этом классе и какие у него есть открытые переменные и свойства. Таким образом, в ООП "черным ящиком" уже становиться не функция, а целый класс, функциональные возможности которого теоретически безграничны. Кроме того, особенности реализации класса позволяют значительно минимизировать вероятность допустить ошибку при его создании. Из всего вышесказанного следует, что классы значительно облегчают и упрощают написание программного кода, что позволяет быстрее и менее трудозатратно писать сложные и объёмные приложения.

Но, как известно, ничего не бывает бесплатным. И за любое удобство нужно платить. Какова же цена за ООП?

А ценой является значительно возросшие требования к ресурсам компьютеров для запуска приложения. И в основном эти требования касаются памяти. Дело в том, что классы порой очень неэффективно используют место в памяти. Очень часто классы создают довольно универсальными, и в программном коде используют только небольшую часть возможного функционала класса. Но класс то при этом в память загружается полностью, со всеми его методами и переменными. Поэтому, довольно большая часть класса может оказаться невостребованной и висеть в памяти мертвым грузом, просто занимая место. Также, во многих языках программирования, которые в своей основе для компиляции кода используют свои виртуальные машины, например тот же C#, Java, Python, для создания класса требуются дополнительные ресурсы процессора, что несколько (а может даже и не несколько) замедляет работу приложения.

2. Три кита объектно-ориентированного программирования

Основная идеология ООП основана на трех принципах: инкапсуляция, наследование и полиморфизм.

Принцип инкапсуляции состоит в том, чтобы скрыть от программиста фактическую реализацию класса. То есть предоставить для работы только открытые методы, свойства и переменные класса, которые позволят полноценно использовать все его предусмотренные возможности, при этом избавив от лишний информации, как этот класс организован изнутри. Другими словами, программист, который реализует готовый класс, должен знать про него ровно столько, сколько ему нужно для его реализации, или модификации. Все остальное ему знать о нем не обязательно.

Принцип наследования заключается в том, чтобы уже готовый класс можно было бы дополнить своими методами и свойствами, или даже полностью заменить в нем реализацию тех или иных уже существующих открытых методов на свой программный код, если это разрешено классом и принципом реализации ООП для конкретного языка программирования. То есть, если есть какой-то класс, который уже реализует определенный нужный нам функционал за исключением некоторых нюансов, то зачем снова придумывать велосипед? От этого класса вполне можно сформировать наследника и дополнить его своими методами и свойствами, и далее уже использовать экземпляр именно этого нового класса, по своему назначению. На практике очень часто множество классов наследуются от одного базового класса, имеющего какой-то общий функционал, который необходим для всех остальных классов. Например. возможность расчета хэш-кода экземпляра класса. Для этого можно создать базовый класс, в котором реализован нужный нам метод расчета хэш-кода, и уже от этого базового класса реализовывать все остальные классы. И тогда все последующие наследники этого класса уже сразу будут иметь возможность рассчитать свой уникальный хэш-код.

Довольно интересным является третий принцип ООП - полиморфизм, но он же и довольно запутанный в плане понимания. Принцип полиморфизма заключается в возможности использования одних и тех же переменных или методов, но используемых в разных классах. То есть он дает возможность создать одну функцию, которая сможет одинаково обрабатывать переданные ей различные классы. Например,  есть четыре сотрудника разных чинов (четыре класса): специалист, заместитель начальника, начальник и директор. Очевидно, что все эти сотрудники будут выполнять каждый свои обязанности (будут иметь различные функции). Но, тем не менее, у каждого сотрудника есть имя, фамилия, отчество, и место работы, и если их вызовет проверяющий (какая то функция) и задаст им всем одни и те же вопросы - где они работают и как их зовут (вызовет метод возврата ФИО и метод возврата наименования организации), то, не смотря на то, что все сотрудники разных чинов, все они представятся, назвав своё имя, фамилию и отчество, и назовут место своей работы. Вот это, грубо говоря, и есть полиморфизм. Если у класса присутствуют нужные нам методы, то этот класс вполне нам подходит, чтобы его использовать.

В языках без строгой типизации, вроде Python, часто упоминается так называемая утиная типизация, которая гласит: "если это выглядит как утка, плавает как утка и крякает как утка, то это, вероятно, и есть утка". То есть, если в классе существуют требуемые методы, то этот класс наверняка является именно тем, который нам и нужен. Но с другой стороны, этот принцип считается если и не ошибкой, то, по крайней мере, предупреждением, и поводом задуматься, а не приведёт ли это к непредвиденной ошибке? Поэтому даже в Python, класс, прежде чем передать его в какую-либо функцию на обработку, лучше все-таки проверить, а тот ли это класс, который собирался использовать?

В языках же со строгой типизацией роль проверки исполнит само наименование класса, вернее это правильнее назвать "тип объекта". А полиморфизм будет обеспечен, вернее даже лучше сказать, что-то вроде узаконен, базовым классом либо интерфейсом, применённым к этому объекту. То есть, передаваемое в функцию значение будет конечно ограничено типом объекта, но, тем не менее, если этот тип унаследован от определенного базового класса, который реализует нужные методы, или он использует определённый интерфейс, который опять же обязывает реализовать эти методы, то экземпляр такого класса вполне будет возможно передать в эту функцию.

Что же это нам в конечном счёте даёт практически? А вот что. Например, есть различные классы, которые открывают некоторые ресурсы, к примеру, открывает для обработки указанный файл, или открывает поток в памяти для перекодировки некоторого текста. Такие ресурсы надо обязательно высвобождать сразу же, как только они выполнили свою функцию, и для работы уже не нужны. Для таких целей часто предусмотрена специальная конструкция, внутри которой можно создать класс, открывающий некий ресурс, но, как только программный код выходит за пределы этой конструкции, эта конструкция автоматически этот ресурс высвободит. Для того чтобы это работало, требуется, чтобы в классе, работающим с ресурсами, был реализован метод Dispose(), который собственно и закрывает открытые ресурсы своего класса. А специальная конструкция просто, после того как класс покидает ее границы, автоматически запускает из этого класса этот метод. Таким образом, если у Вас есть готовый класс, и Вы хотите его использовать в этой специальной конструкции, то пожалуйста, Вам надо всего лишь указать, что Ваш класс реализует соответствующий интерфейс, который уже обяжет Вас реализовать нужный метод, в данном случае Dispose(). И далее Вы уже можете спокойно использовать класс внутри этой конструкции, и при выходе Вашего класса из этой конструкции, как и из любого другого, реализующего соответствующий интерфейс, будет запущен метод Dispose(). Но, Вы должны конечно же понимать, будут ли действительно в этой конструкции высвобождены ресурсы, или нет, это зависеть уже от того, как Вы реализуете требуемый метод Dispose(). Собственно реализовать в нём вы можете все что угодно, и совсем не обязательно, что это должно быть именно высвобождения ресурсов. Это может быть даже просто обычное уведомление о том, что класс, допустим, прекратил свою работу.

3. "Привет мир"

И так, от теории перейдём к практике. И для начала, для практического ознакомления с ООП, я хотел бы продемонстрировать очень простой класс, который только и умеет то, что поприветствовать наш мир, и подсчитать, сколько раз он это сделал. Когда Вы только начинали изучать программирование, Вы уже почти наверняка "приветствовали мир" с помощью простенькой процедуры, теперь же я хочу продемонстрировать примерно то же самое, но уже посредством класса:

  • Код на C++
  • Код на C#
  • Код на Java
  • Код на Python 3
/** Файл main.cpp **************************/
#include <stdio.h>
#include "NewClass.h"

int main()
{
    NewClass newClass;
    newClass.message();
    newClass.message();
    
    return 0;
}

/** Файл NewClass.h **************************/
class NewClass {
private:
    int index;
    
public:
     /** Конструктор */
     NewClass();
     void message();
};

/** Файл NewClass.cpp **************************/
#include <stdio.h>
#include "NewClass.h"

/** Конструктор */
NewClass::NewClass() {
    index = 0;
}

void NewClass::message() {
    index ++;
    printf("Hello World %d\n", index);
}
using System;
public class MainClass {
  public static void Main() {
    NewClass newClass = new NewClass();
    newClass.message();
    newClass.message();
  }
}

class NewClass {
    private int index;
    
    /** Конструктор */
    public NewClass() {
        index = 0;
    }
    
    public void message() {
        index ++;
        Console.WriteLine("Hello World " + index);
    }
}
public class Main {
    public static void main(String[] args) {
        NewClass newClass = new NewClass();
        newClass.message();
        newClass.message();
    }
}

class NewClass {
    private int index;
    
    /** Конструктор */
    public NewClass() {
        index = 0;
    }
    
    public void message() {
        index ++;
        System.out.println("Hello World " + index);
    }
}
class NewClass:
    def __init__(self):
        """ Конструктор """
        self._index = 0

    def message(self):
        self._index += 1
        print("Hello World {}".format(self._index))


def main():
        new_class = NewClass()
        new_class.message()
        new_class.message()


if __name__ == '__main__':
    main()

Вот результат работы этого программного кода.

Hello World 1
Hello World 2 

Этот класс состоит всего лишь из конструктора, который инициализирует счётчик приветствий, одной закрытой переменной int index, с помощью которой и ведётся подсчёт количества приветствий, и одного открытого метода message(), который, собственно, и выводит на экран сообщение о приветствии.

4. Коротко о главном

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

Класс может состоять из методов, это те же функции, но созданные внутри класса, переменных, и свойств. Методы, переменные и свойства могут иметь один из трёх основных модификатора доступа:

  • Модификатор public означает, что метод (переменная или свойство), обозначенный таким модификатором, является открытым, и он будет доступен извне класса. Именно такие методы и представляют основной функционал класса, которые потом используют программисты в своём программном коде. Кроме того, эти методы в дальнейшем, в некоторых случаях, возможно изменять или дополнять своим кодом в классах наследниках от этого класса.
  • Модификатор private обозначает закрытый метод (переменная или свойство) который извне класса не доступен. С помощью таких методов программист-разработчик класса реализует основную внутреннюю логику работы этого класса и по принципу инкапсуляции обычным программистам знание о существовании таких методов совсем не обязательно. Так как эти методы закрытые, то их нельзя использовать, изменять, и вообще они в классах наследниках не доступны и не видны.
  • Модификатором protected обозначают, что метод (переменная или свойство) является защищенным. Такие методы также не доступны извне класса, но зато они видны и их вполне можно использовать по своему желанию, при создании своих собственных классов наследников. Так же при создании классов наследников такие методы, в некоторых случаях, возможно изменять или дополнять своим программным кодом.

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

Как я уже упомянул выше, класс может состоять из методов, переменных и свойств. Но, если с методами и переменными всё более менее понятно, то что же такое свойство (Property)? А свойство это что-то среднее между методом и переменной. Хотя, надо заметить, что свойства реализованы далеко не во всех языках программирования, и часто свойства заменяются сеттерами и геттерами (вернее это скорее наоборот в некоторых языках программирования сеттеры и геттеры в дальнейшем для удобства заменили свойствами). С одной стороны к свойству обращаются как к переменной, то есть изменяют значение свойства с  помощью обычного знака равенства, и  получает значения свойства без указания скобок.

MyClass.Property1 = 99;
int num = MyClass.Property1;

Но с другой стороны, при изменении значения свойства, в отличии от переменной, есть возможность проверить новое значение передаваемое свойству, и отклонить его либо скорректировать. Чаще всего свойство реализуется посредством закрытой переменной. То есть само по себе свойство это все же открытый метод, вернее даже пара методов. Вообще в классах не принято вне самого класса напрямую обращаться к его переменным (хотя Вам никто это и не запрещает), поэтому и переменные чаще всего создают закрытые, их не видно извне класса. А изменяют переменные с помощью специальной конструкции называемой сеттеры, или свойства, что, по сути, является тем же самым. Это открытый метод, видимый извне класса, и к нему можно свободно обращаться. Сделано это по той причине, что напрямую переменной можно передать все что угодно, ну разве что может быть ограничено типом переменной. Но часто бывают ситуации, когда значения переменной строго ограничено, например, только положительными значениями, или значением от 0 до 1. И если передать неправильное значение, то это, скорее всего, неминуемо приведёт к ошибке. Чтобы избежать такой ошибки, прежде чем передать значение переменной, это значение проверяется на корректность ввода, и только после проверки передается переменной. Именно этим и занимается сеттер, или свойство. Сеттер это именно метод, и принимает значение через передачу параметра метода внутри скобок. Свойство же, хоть и является таким же методом, и тоже принимает параметр, но в коде принимает значение обычным равенством, что просто красивее, и может быть удобнее. Далее, так как переменная закрытая, то чтобы получить ее значение, снова нужен открытый метод, который вернет значение этой переменной. Этим занимается уже геттер. Или же снова свойство, которое делает абсолютно то же самое, но опять же без указания скобок. Перед возвращением значения переменной, это значение при желании можно скорректировать или изменить, или вообще сгенерировать и вернуть некоторое значение прямо на лету.

Чтобы стало более понятно, вот небольшой практический пример:

  • Код на C++
  • Код на C#
  • Код на Java
  • Код на Python 3
/** Файл main.cpp **************************/
#include <stdio.h>
#include "NewClass.h"

int main()
{
    NewClass newClass;
    newClass.setPositiveProperty(-10);
    printf("Положительное свойство %d\n", newClass.getPositiveProperty());
    
    newClass.setPositiveProperty(20);
    printf("Положительное свойство %d\n", newClass.getPositiveProperty());
    
    return 0;
}
/** Файл NewClass.h **************************/
class NewClass {
private:
    /** Закрытая переменная */
    int positiveProperty;
    
public:
    /** Конструктор класса */
     NewClass();
    /** Сеттер */
     void setPositiveProperty(int val);
    /** Геттер */
     int getPositiveProperty();
};

/** Файл NewClass.cpp **************************/
#include <stdio.h>
#include "NewClass.h"

/** Конструктор класса */
NewClass::NewClass() {
    positiveProperty = 0;
}

/** Сеттер */
void NewClass::setPositiveProperty(int value) {
    if (value < 0)
        value = value * -1;
   positiveProperty = value;
}

/** Геттер */
int NewClass::getPositiveProperty() {
    return positiveProperty;
}
using System;
public class MainClass {
    public static void Main() {
        NewClass newClass = new NewClass();
        newClass.PositiveProperty = -10;
        Console.WriteLine("Положительное свойство {0}", newClass.PositiveProperty);
        
        newClass.PositiveProperty = 20;
        Console.WriteLine("Положительное свойство {0}", newClass.PositiveProperty);
    }
}

class NewClass {
    // Закрытая переменная
    private int positiveProperty;
    
    /** Конструктор класса */
    public NewClass() {
        positiveProperty = 0;
    }
    /** Свойство */
    public int PositiveProperty {
        // Сеттер
        set {
            if (value < 0)
                value = value * -1;
            positiveProperty = value;
        }
        // Геттер        
        get {
            return positiveProperty;
            
        }
    }
}
public class Main {
    public static void main(String[] args) {
        NewClass newClass = new NewClass();
        newClass.setPositiveProperty(-10);
        System.out.printf("Положительное свойство %d\n", newClass.getPositiveProperty());
        
        newClass.setPositiveProperty(20);
        System.out.printf("Положительное свойство %d\n", newClass.getPositiveProperty());
    }
}

class NewClass {
    // Закрытая переменная
    private int positiveProperty;
    
    /** Конструктор класса */
    public NewClass() {
        positiveProperty = 0;
    }
    
    /** Сеттер */
    public void setPositiveProperty(int value) {
        if (value < 0)
            value = value * -1;
        positiveProperty = value;
    }
    
    /** Геттер */
    public int getPositiveProperty() {
        return positiveProperty;
    }
}
class NewClass:
    def __init__(self):
        """ Конструктор класса """
        # Закрытая переменная
        self._positive_property = 0
    
    @property
    def positive_property(self) -> str:
        """ Свойство. Геттер. """
        return self._positive_property
    
    @positive_property.setter
    def positive_property(self, value: int):
        """ Свойство. Сеттер. """
        if value < 0:
            value = value * -1
        self._positive_property = value


def main():
        new_class = NewClass()
        new_class.positive_property = -10
        print("Положительное свойство", new_class.positive_property)
        
        new_class.positive_property = 20
        print("Положительное свойство", new_class.positive_property)


if __name__ == '__main__':
    main()

и результат его работы:

Положительное свойство 10 
Положительное свойство 20 

Как можно увидеть из кода выше, в C++ и Java свойства класса не реализованы, поэтому в этих языках используются сеттеры и геттеры, а в C# и Python 3 свойства присутствуют, поэтому вместо сеттеров и геттеров можно использовать их.

В классе также довольно часто присутствует особый метод, наименование которого чаще всего (но не всегда) совпадает с наименованием самого класса. Этот метод является открытым, и он автоматически запускается каждый раз, когда создаётся новый экземпляр этого класса. Такой метод называется - конструктор класса. Назначением конструктора является установка начального состояния класса при создании его экземпляра, то есть инициализация и определение начальных значений его переменных. В конструктор класса при его создании можно передавать параметры, с помощью которых, как правило, и определяется начальное состояние создаваемого класса.

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

Что бы определить метод как статический, надо добавить ему модификатор static. У статических методов есть одно существенное ограничение, такие методы не могут содержать в своём коде нестатические методы и переменные своего класса, хотя нестатические методы вполне могут запускать статические методы класса и использовать его статические переменные. Ещё не стоит забывать, что статические методы так же имеют модификаторы доступа, и чтобы ими воспользоваться вне класса, они должен быть открытым.

5. Класс "Фигуры", начало

Теперь, после того как общая информация о классах уже известна, наконец настало время создать какой-нибудь реальный класс. И начну я с создания класса прямоугольника (Rectangle).

А что из себя представляет прямоугольник? Это фигура имеющая ширину, высоту, и, допустим, цвет. Вот это и нужно реализовать в классе. Ещё было бы неплохо, если бы эта фигура умела отображать о себе какую-нибудь информацию. Например, так как у неё есть размер, то пускай она будет уметь отображать свою площадь и длину периметра. Параметры фигуры, то есть её размер и цвет, будут определяться при её создании. Ширина и высота должны быть целочисленными, а цвет фигуры должен назначаться в виде шестнадцатеричного целого числа, в соответствии со стандартом представления цифровых RGB-цветов. После создания фигуры её размер уже меняться не должен, а вот возможность изменения цвета быть должна. Вот примерная реализация такого класса:

  • Код на C++
  • Код на C#
  • Код на Java
  • Код на Python 3
/** Файл main.cpp **************************/
#include <stdio.h>
#include "MyClasses.h"

int main()
{
    // Использование класса Прямоугольник.
    Rectangle rectangle(2, 3, RED);
    printf("Прямоугольник:\n");
    rectangle.ShowArea();
    rectangle.ShowLength();
    rectangle.ShowColor();
    printf("----------------------------\n");

    return 0;
}


/** Файл MyClasses.h **************************/
const int WHITE = 0xFFFFFF;
const int BLACK = 0x000000;
const int RED =   0xFF0000;
const int GREEN = 0x00FF00;
const int BLUE =  0x0000FF;

/** Класс прямоугольника */
class Rectangle {
private:
    int height,
        width,
        color;
    int Area();
    int Length();
    
public:
     Rectangle();
     Rectangle(int height, int width, int color);
     void ShowArea();
     void ShowLength();
     void SetColor(int color);
     void ShowColor();
};

/** Файл MyClasses.cpp **************************/
#include <stdio.h>
#include "MyClasses.h"

Rectangle::Rectangle(): Rectangle(1, 1, BLACK) {}

Rectangle::Rectangle(int height, int width, int color) {
    this->height = height;
    this->width = width;
    this->color = color;
}

/** 
* Возвращает площадь фигуры 
*/
int Rectangle::Area() {
    return height * width;
}

/** 
* Возвращает длину периметра фигуры 
*/
int Rectangle::Length() {
    return (height + width) * 2;
}

/**
* Отображает площадь
*/
void Rectangle::ShowArea() {
    printf("Площадь: %d\n", Area());
}
        
/**
* Отображает длину периметра
*/
void Rectangle::ShowLength() {
    printf("Длина: %d\n", Length());
}

/**
* Отображает цвет
*/
 void Rectangle::ShowColor() {
    printf("Цвет: #%06X\n", color);
}
using System;
public class MainClass {
    const int WHITE = 0xFFFFFF;
    const int BLACK = 0x000000;
    const int RED =   0xFF0000;
    const int GREEN = 0x00FF00;
    const int BLUE =  0x0000FF;
    
    public static void Main() {
        // Использование класса Прямоугольник
        Rectangle rectangle = new Rectangle(2, 3, RED);
        Console.WriteLine("Прямоугольник:");
        rectangle.ShowArea();
        rectangle.ShowLength();
        rectangle.ShowColor();
        Console.WriteLine("----------------------------\n");
       
    }
    
    /** Класс прямоугольник */
    class Rectangle {
        private int height;
        private int width;
        private int color;
        
        public Rectangle() : this(1, 1, BLACK) {}
        
        public Rectangle(int height, int width, int color) {
           this.height = height;
           this.width = width;
           this.color = color;
        }
        
        /** 
        * Возвращает площадь фигуры 
        */
        private int Area() {
            return height * width;
        }
        
        /** 
        * Возвращает длину периметра фигуры 
        */
        private int Length() {
            return (height + width) * 2;
        }
        
        /**
        * Отображает площадь
        */
        public void ShowArea() {
            Console.WriteLine("Площадь: {0}", Area());
        }
        
        /**
        * Отображает длину периметра
        */
        public void ShowLength() {
            Console.WriteLine("Длина: {0}", Length());
        }
        
        /**
        * Устанавливает цвет
        */
        public void SetColor(int color) {
            this.color = color;
        }
        
        /**
        * Отображает цвет
        */
        public void ShowColor() {
            Console.WriteLine("Цвет: #{0:X6}", color);
        }
    }
}
public class Main {
    static final int WHITE = 0xFFFFFF;
    static final int BLACK = 0x000000;
    static final int RED =   0xFF0000;
    static final int GREEN = 0x00FF00;
    static final int BLUE =  0x0000FF;
    
    public static void main(String[] args) {
        Main main = new Main();
        main.Start();
    }
    
    public void Start() {
        Rectangle rectangle = new Rectangle(2, 3, RED);
        System.out.println("Прямоугольник:");
        rectangle.ShowArea();
        rectangle.ShowLength();
        rectangle.ShowColor();
        System.out.println("----------------------------\n");
    }

    /** Класс прямоугольника */
    class Rectangle {
        private int height;
        private int width;
        private int color;
        
        public Rectangle() {
            this(1, 1, BLACK);
        }
        
        public Rectangle(int height, int width, int color) {
           this.height = height;
           this.width = width;
           this.color = color;
        }
        
        /** 
        * Возвращает площадь фигуры 
        */
        private int Area() {
            return height * width;
        }
        
        /** 
        * Возвращает длину периметра фигуры 
        */        
        private int Length() {
            return (height + width) * 2;
        }
        
        /**
        * Отображает площадь
        */        
        public void ShowArea() {
            System.out.printf("Площадь: %d\n", Area());
        }
        
        /**
        * Отображает длину периметра
        */        
        public void ShowLength() {
            System.out.printf("Длина: %d\n", Length());
        }

        /**
        * Устанавливает цвет
        */        
        public void SetColor(int color) {
            this.color = color;
        }
        
        /**
        * Отображает цвет
        */        
        public void ShowColor() {
            System.out.printf("Цвет: #%06X\n", color);
        }
    }
}
WHITE = 0xFFFFFF
BLACK = 0x000000
RED = 0xFF0000
GREEN = 0x00FF00
BLUE = 0x0000FF


class Rectangle:
    """  Класс прямоугольник """
    def __init__(self, height: int=1, width: int=1, color: int=BLACK):
        self._height = height
        self._width = width
        self._color = color

    def _area(self) ->int:
        """ Возвращает площадь фигуры """
        return self._height * self._width

    def _length(self) -> int:
        """ Возвращает длину периметра фигуры """
        return (self._height + self._width) * 2

    def show_area(self):
        print("Площадь {0}". format(self._area()))

    def show_length(self):
        print("Длинна {0}". format(self._length()))

    def set_color(self, color: int):
        self._color = color

    def show_color(self):
        print("Цвет #{0:06X}".format(self._color))


def main():
    # Использование класса Прямоугольник
    rectangle = Rectangle(2, 3, RED)
    print("Прямоугольник:")
    rectangle.show_area()
    rectangle.show_length()
    rectangle.show_color()
    print("----------------------------\n")


if __name__ == '__main__':
    main()

и результат его работы:

Прямоугольник:                                        
Площадь: 6                                            
Длина: 10                                            
Цвет: #FF0000                                          
----------------------------  

И так, что же есть в этом классе? А в этом классе есть три закрытые целочисленные переменные, в которых храниться информация о ширине прямоугольника, длине и его цвете. Так как изменять размеры прямоугольника мы не планируем, получать эти размеры нам тоже не интересно, поэтому эти переменные имеют модификатор private, и доступ к ним закрыт. А устанавливаются размеры исключительно через конструктор, указав в нём при создании класса параметры ширины и длинны фигуры. Там же задаётся и цвет фигуры.

/** Конструктор со значениями по умолчанию */
public Rectangle() : this(1, 1, BLACK) {}
/** Конструктор ожидающий параметров */
public Rectangle(int height, int width, int color) {
   this.height = height;
   this.width = width;
   this.color = color;
}
Rectangle rectangle = new Rectangle(2, 3, RED);

В данном случае перед присваиванием размеров было бы разумно для начала сделать проверку переданных параметров на корректность ввода, чтобы, например, не создать прямоугольник с отрицательными размерами. Сделать это можно различными способами, например такую проверку можно просто произвести прямо в конструкторе класса. А можно создать отдельные методы для присваивания значения, что то вроде сеттеров, но с одним отличием - так как они будут нужны только для внутренней реализации логики, и программистам об этих методах знать совсем не нужно, то лучше сделать их закрытыми, то есть с модификатором private.

Но в моём случае такая проверка не требуется, поэтому я всё оставлю как есть.

Кстати, хочу заметить, что в данном классе присутствуют два конструктора, один - через который можно задать параметры прямоугольника, а второй с начальными параметрами по умолчанию, если класс фигуры будет создаваться вообще без передачи параметров. Такая конструкция называется перегрузка конструктора. В принципе перегрузить можно любые методы (если это позволяет ООП конкретного языка), предоставляя пользователю возможность использовать один и тот же метод с разным набором входным параметров

Далее, переменная, в которой храниться цвет фигуры int color, тоже имеет модификатор private, но возможность изменить её имеется, и для этого предусмотрен открытый метод SetColor(int color), хотя он пока что и не используется.

Для отображения информации о фигуре реализованы три открытых метода: ShowArea() - для отображения информации о площади фигуры, ShowLength() - для отображения информации о длине периметра фигуры и ShowColor() - для отображения информации о цвете фигуры. В нашем случае это как раз именно те методы, которые представляют функционал класса, и которые предоставлены пользователям для их использования.

Для того чтобы отобразить информацию о площади фигуры и о длине её периметра, эти значения нужно вначале рассчитать. Для этого созданы два закрытых метода int Area(), который рассчитывает и возвращает площадь фигуры, и int Length(), который соответственно рассчитывает и возвращает длину периметра фигуры. Эти методы можно отнести к внутренней логике работы класса, ведь именно в них производятся нужные нам расчёты, из-за чего мы собственно и приняли решения использовать для своих целей именно этот класс. Каким образом эти расчёты производятся, пользователю знать не обязательно, поэтому эти методы имеют модификатор private, они не доступны, не видны и не могут быть изменены, что, кстати, в какой-то мере позволяет гарантировать правильность расчётов, предохраняя программный код от случайного в него вмешательства.

Для использования класса вначале создаётся его экземпляр, после чего этому экземпляру класса станут доступны все его открытые методы, переменные и свойства:

Rectangle rectangle = new Rectangle(2, 3, RED);
Console.WriteLine("Прямоугольник:");
rectangle.ShowArea();
rectangle.ShowLength();
rectangle.ShowColor();
Console.WriteLine("----------------------------\n");

6. Наследование

Ну вот, класс прямоугольника Rectangle и создан. Но что, если теперь требуется создать класс квадрата? Да, можно просто снова воспользоваться классом Rectangle, передав ему одинаковые значения длинны и ширины. Но все-таки прямоугольник есть прямоугольник, а квадрат - есть квадрат. Тем более, что создать класс квадрата, имея другой, очень похожий класс, не составит никакого труда:

  • Код на C++
  • Код на C#
  • Код на Java
  • Код на Python 3
/** Файл Square.h **************************/
#include "Rectangle.h"

/** Класс Квадрат */
class Square : public Rectangle {
public:
    Square();
    Square(int height, int color);
};

/** Файл Square.cpp **************************/
#include "Rectangle.h"
Square::Square(): Rectangle() {}
Square::Square(int height, int color): Rectangle(height, height, color) {}
/** Класс Квадрат */
class Square : Rectangle {
    public Square() : base() {}
 
    public Square(int height, int color) : base(height, height, color) {
    }
}
/** Класс Квадрат */
class Square extends Rectangle {
    public Square() {
        super();
    }
        
    public Square(int height, int color) {
        super(height, height, color);
    }
}
class Square(Rectangle):
    """ Класс Квадрат """
    def __init__(self, height: int=1, color: int=BLACK):
        super().__init__(height, height, color)

Вот новый класс и готов, и все что для этого понадобилось, это наследоваться от класса Rectangle, и реализовать для него новый конструктор класса, оставив в нём только один параметр размера - высоту. Этот новый конструктор обращается к конструктору базового класса, то есть класса Rectangle, передавая уже в его конструктор в параметр ширины и высоты свой единственный параметр размера. Параметр цвета, разумеется, остался неизменным, квадрату тоже нужен цвет. Этот новый класс Квадрат (Square) унаследовал у прямоугольника все его открытые методы, и мы снова их можем использовать, но уже в новом классе. Вот он полный код создания класса Square, а так же его использование:

  • Код на C++
  • Код на C#
  • Код на Java
  • Код на Python 3
/** main.cpp **************************/
#include <stdio.h>
#include "MyClasses.h"

int main()
{
    Rectangle rectangle(2, 3, RED);
    printf("Прямоугольник:\n");
    rectangle.ShowArea();
    rectangle.ShowLength();
    rectangle.ShowColor();
    printf("----------------------------\n\n");
    
    Square square(5, GREEN);
    printf("Квадрат:\n");
    square.ShowArea();
    square.ShowLength();
    square.ShowColor();
    printf("----------------------------\n");
        
    return 0;
}

/** Файл MyClasses.h **************************/
const int WHITE = 0xFFFFFF;
const int BLACK = 0x000000;
const int RED =   0xFF0000;
const int GREEN = 0x00FF00;
const int BLUE =  0x0000FF;


class Rectangle {
private:
    int height,
        width,
        color;
    int Area();
    int Length();
    
public:
     Rectangle();
     Rectangle(int height, int width, int color);
     void ShowArea();
     void ShowLength();
     void SetColor(int color);
     void ShowColor();
};

class Square : public Rectangle {
public:
    Square();
    Square(int height, int color);
};

/** Файл MyClasses.cpp **************************/
#include <stdio.h>
#include "MyClasses.h"

Rectangle::Rectangle(): Rectangle(1, 1, BLACK) {}

Rectangle::Rectangle(int height, int width, int color) {
    this->height = height;
    this->width = width;
    this->color = color;
}

/** 
* Возвращает площадь фигуры 
*/
int Rectangle::Area() {
    return height * width;
}

/** 
* Возвращает длину периметра фигуры 
*/
int Rectangle::Length() {
    return (height + width) * 2;
}

/**
* Отображает площадь
*/
void Rectangle::ShowArea() {
    printf("Площадь: %d\n", Area());
}
        
/**
* Отображает длину периметра
*/
void Rectangle::ShowLength() {
    printf("Длина: %d\n", Length());
}

/**
* Отображает цвет
*/
 void Rectangle::ShowColor() {
    printf("Цвет: #%06X\n", color);
}

Square::Square(): Rectangle() {}

Square::Square(int height, int color): Rectangle(height, height, color) {}
using System;

public class MainClass {
    const int WHITE = 0xFFFFFF;
    const int BLACK = 0x000000;
    const int RED =   0xFF0000;
    const int GREEN = 0x00FF00;
    const int BLUE =  0x0000FF;
    
    public static void Main() {
        Rectangle rectangle = new Rectangle(2, 3, RED);
        Console.WriteLine("Прямоугольник:");
        rectangle.ShowArea();
        rectangle.ShowLength();
        rectangle.ShowColor();
        Console.WriteLine("----------------------------\n");
        
        Square square = new Square(5, GREEN);
        Console.WriteLine("Квадрат:");
        square.ShowArea();
        square.ShowLength();
        square.ShowColor();
        Console.WriteLine("----------------------------\n");
        
    }
    
    class Rectangle {
        private int height;
        private int width;
        private int color;
        
        public Rectangle() : this(1, 1, BLACK) {}
        
        public Rectangle(int height, int width, int color) {
           this.height = height;
           this.width = width;
           this.color = color;
        }
        
        /** 
        * Возвращает площадь фигуры 
        */
        private int Area() {
            return height * width;
        }
        
        /** 
        * Возвращает длину периметра фигуры 
        */
        private int Length() {
            return (height + width) * 2;
        }
        
        /**
        * Отображает площадь
        */
        public void ShowArea() {
            Console.WriteLine("Площадь: {0}", Area());
        }
        
        /**
        * Отображает длину периметра
        */
        public void ShowLength() {
            Console.WriteLine("Длина: {0}", Length());
        }
        
        /**
        * Устанавливает цвет
        */
        public void SetColor(int color) {
            this.color = color;
        }
        
        /**
        * Отображает цвет
        */
        public void ShowColor() {
            Console.WriteLine("Цвет: #{0:X6}", color);
        }
    }

    class Square : Rectangle {
        public Square() : base() {}
        
        public Square(int height, int color) : base(height, height, color) {
        }
    }
}
public class Main {
    static final int WHITE = 0xFFFFFF;
    static final int BLACK = 0x000000;
    static final int RED =   0xFF0000;
    static final int GREEN = 0x00FF00;
    static final int BLUE =  0x0000FF;
    
    public static void main(String[] args) {
        Main main = new Main();
        main.Start();
    }
    
    public void Start() {
        Rectangle rectangle = new Rectangle(2, 3, RED);
        System.out.println("Прямоугольник:");
        rectangle.ShowArea();
        rectangle.ShowLength();
        rectangle.ShowColor();
        System.out.println("----------------------------\n");
        
        Square square = new Square(5, GREEN);
        System.out.println("Квадрат:");
        square.ShowArea();
        square.ShowLength();
        square.ShowColor();
        System.out.println("----------------------------\n");
    }
	
	class Rectangle {
        private int height;
        private int width;
        private int color;
        
        public Rectangle() {
            this(1, 1, BLACK);
        }
        
        public Rectangle(int height, int width, int color) {
           this.height = height;
           this.width = width;
           this.color = color;
        }
        
        /** 
        * Возвращает площадь фигуры 
        */
        private int Area() {
            return height * width;
        }
        
        /** 
        * Возвращает длину периметра фигуры 
        */        
        private int Length() {
            return (height + width) * 2;
        }
        
        /**
        * Отображает площадь
        */        
        public void ShowArea() {
            System.out.printf("Площадь: %d\n", Area());
        }
        
        /**
        * Отображает длину периметра
        */        
        public void ShowLength() {
            System.out.printf("Длина: %d\n", Length());
        }

        /**
        * Устанавливает цвет
        */        
        public void SetColor(int color) {
            this.color = color;
        }
        
        /**
        * Отображает цвет
        */        
        public void ShowColor() {
            System.out.printf("Цвет: #%06X\n", color);
        }
    }
    
    class Square extends Rectangle {
        public Square() {
            super();
        }
        
        public Square(int height, int color) {
            super(height, height, color);
        }
    }
}
WHITE = 0xFFFFFF
BLACK = 0x000000
RED = 0xFF0000
GREEN = 0x00FF00
BLUE = 0x0000FF


class Rectangle:
    def __init__(self, height: int=1, width: int=1, color: int=BLACK):
        self._height = height
        self._width = width
        self._color = color

    def _area(self) ->int:
        """ Возвращает площадь фигуры """
        return self._height * self._width

    def _length(self) -> int:
        """ Возвращает длину периметра фигуры """
        return (self._height + self._width) * 2

    def show_area(self):
        print("Площадь {}".format(self._area()))

    def show_length(self):
        print("Длинна {}".format(self._length()))

    def set_color(self, color: int):
        self._color = color

    def show_color(self):
        print("Цвет #{0:06X}".format(self._color))


class Square(Rectangle):
    def __init__(self, height: int=1, color: int=BLACK):
        super().__init__(height, height, color)


def main():
    rectangle = Rectangle(2, 3, RED)
    print("Прямоугольник:")
    rectangle.show_area()
    rectangle.show_length()
    rectangle.show_color()
    print("----------------------------\n")

    square = Square(5, GREEN)
    print("Квадрат:")
    square.show_area()
    square.show_length()
    square.show_color()
    print("----------------------------\n")


if __name__ == '__main__':
    main()

Результат работы:

Прямоугольник:
Площадь: 6
Длина: 10
Цвет: #FF0000 
----------------------------

Квадрат:
Площадь: 25
Длина: 20
Цвет: #00FF00
---------------------------- 

7. Абстрактный класс

Продолжаем тему фигур. Теперь на очереди ещё один класс, но на этот раз нужен класс Круг (Circle). Возможности у него должны быть такие же, как и у прямоугольника с квадратом, то есть он должен уметь отображать свою площадь, длину периметра и цвет. Можно было бы опять же наследоваться от Rectangle, но, увы, расчёт площади и длины периметра у круга отличаются от тех же расчётов, что у прямоугольника, а методы расчетов площади и длинны периода в классе Rectangle, как мы помним, закрытые и изменению не подлежат. Ну что же, попробуем тогда создать новый, самостоятельный класс Circle:

  • Код на C++
  • Код на C#
  • Код на Java
  • Код на Python 3
/** Файл Circle.h **************************/
const int WHITE = 0xFFFFFF;
const int BLACK = 0x000000;
const int RED =   0xFF0000;
const int GREEN = 0x00FF00;
const int BLUE =  0x0000FF;

class Circle {
private:
    int radius,
        color;
    double Area();
    double Length();
    
public:
    const double pi = 3.14;
    Circle();
    Circle(int radius, int color);
    void ShowArea();
    void ShowLength();
    void SetColor(int color);
    void ShowColor();
};

/** Файл Circle.cpp **************************/
#include <stdio.h>

Circle::Circle(): Circle(1, BLACK) {}

Circle::Circle(int radius, int color) {
    this->radius = radius;
    this->color = color;
}

/** 
* Возвращает площадь фигуры 
*/
double Circle::Area() {
    return pi * (radius * radius);
}

/** 
* Возвращает длину периметра фигуры 
*/
double Circle::Length() {
    return 2 * pi * radius;
}

/**
* Отображает площадь
*/
void Circle::ShowArea() {
    printf("Площадь: %.2f\n", Area());
}
        
/**
* Отображает длину периметра
*/
void Circle::ShowLength() {
    printf("Длина: %.2f\n", Length());
}

/**
* Устанавливает цвет
*/
void Circle::SetColor(int color) {
    this->color = color;
}

/**
* Отображает цвет
*/
 void Circle::ShowColor() {
    printf("Цвет: #%06X\n", color);
}
    const int WHITE = 0xFFFFFF;
    const int BLACK = 0x000000;
    const int RED =   0xFF0000;
    const int GREEN = 0x00FF00;
    const int BLUE =  0x0000FF;
 
   class Circle {
        const double pi =  3.14;
        private int radius;
        private int color;
        
        public Circle() : this(1, BLACK) {}
        
        public Circle(int radius, int color) {
            this.radius = radius;
            this.color = color;
        }
        
        /** 
        * Возвращает площадь фигуры 
        */
        private double Area() {
            return pi * (radius * radius);
        }
        
        /** 
        * Возвращает длину периметра фигуры 
        */
        private double Length() {
            return 2 * pi * radius;
        }
        
        /**
        * Отображает площадь
        */
        public void ShowArea() {
            Console.WriteLine("Площадь: {0:f}", Area());
        }
        
        /**
        * Отображает длину периметра
        */
        public void ShowLength() {
            Console.WriteLine("Длина: {0:f}", Length());
        } 
        
        /**
        * Устанавливает цвет
        */
        public void SetColor(int color) {
            this.color = color;
        }
        
        /**
        * Отображает цвет
        */
        public void ShowColor() {
            Console.WriteLine("Цвет: #{0:X6}", color);
        }
    }
    static final int WHITE = 0xFFFFFF;
    static final int BLACK = 0x000000;
    static final int RED =   0xFF0000;
    static final int GREEN = 0x00FF00;
    static final int BLUE =  0x0000FF;

    class Circle {
        final double pi = 3.14;
        private int radius;
        private int color;
        
        public Circle() {
            this(1, BLACK);
        }
        
        public Circle(int radius, int color) {
            this.radius = radius;
            this.color = color;
        }
        
        private double Area() {
            return pi * (radius * radius);
        }
        
        private double Length() {
            return 2 * pi * radius;
        }
        
        public void ShowArea() {
            System.out.println("Площадь: " + Area());
        }
        
        public void ShowLength() {
            System.out.println("Длина: " + Length());
        }
        
        public void SetColor(int color) {
            this.color = color;
        }
        
        public void ShowColor() {
            System.out.printf("Цвет: #%06X\n", color);
        }
    }
class Circle:
    def __init__(self, radius: int=1, color: int=BLACK):
        self._pi = 3.14
        self._radius = radius
        self._color = color

    def _area(self) -> float:
        """ Возвращает площадь фигуры """
        return self._pi * (self._radius * self._radius)

    def _length(self) -> float:
        """ Возвращает длину периметра фигуры """
        return 2 * self._pi * self._radius

    def show_area(self):
        print("Площадь {0:.2f}".format(self._area()))

    def show_length(self):
        print("Длинна {0:.2f}".format(self._length()))

    def set_color(self, color: int):
        self.__color = color

    def show_color(self):
        print("Цвет #{0:06X}".format(self._color))

И хотя этот класс очень похож на класс Rectangle, и открытые методы он предоставляет точно такие же, но, тем не менее, с точки зрения ОПП это два абсолютно разных, ничем между собой не связанных, класса. А жаль. В программирование довольно часто бывает очень удобно проводить различные конвейерные операции, вроде объединить множество однотипных объектов в один список и передать его на обработку в какую-либо функцию, которая бы смогла бы весь это список обработать, проделав некую нужную операцию отдельно над каждым объектом. К классам это тоже вполне можно применить. Например, в одном из предыдущих программных кодов я вручную отображал информацию отдельно вначале по прямоугольнику, а затем по квадрату, фактически дублируя один и тот же код. А если бы таких прямоугольников и квадратов было десть экземпляров каждого, или еще больше? Тут явно нужно изменять код, чтобы не нарушать один из главных принципов программирования, принцип DRY (Don’t repeat yourself, рус. не повторяйся). Например, можно было бы сделать примерно вот такую функцию для отображения переданного массива квадратов:

static void ShowFigures(Rectangle[] rectangles) {
    foreach(Rectangle rectangle in rectangles) {
        rectangle.ShowArea();
        rectangle.ShowLength();
        rectangle.ShowColor();
        Console.WriteLine("----------------------------\n");
    }
}

и таким вот образом ее реализовать:

public static void Main() {
    Rectangle rectangle = new Rectangle(2, 3, RED);
    Square square = new Square(5, GREEN);
    Rectangle[] rectangles = {rectangle, square};
    ShowFigures(rectangles); }

Здесь только не хватает отображения наименования фигуры, информация о которой выводится, так как мы такую информацию в классе нигде и не предусмотрели. Но это дело поправимое. Главное, что теперь такая функция сможет отобразить информацию о любом количестве переданных в нее массиве фигур. А как же круг? Он же тоже фигура, и имеете те же методы, которые использованы в функции? Нет, круг, в том виде какой он есть, в этот массив добавить не удастся (разве что это можно сделать в Python, так как в нем нет строгой типизации данных, но зато у класса наблюдается явная утиная типизация). А ведь это было бы правильным, информацию о круге отображать одинаково вместе с двумя другими фигурами.

Чтобы это стало возможно сделать, надо и круг , и квадрат, и прямоугольник наследовать от какого-то одного класса, который бы реализовывал нужные для отображения информации открытые методы. И этим классом будет класс Фигура (Figure):

  • Код на C++
  • Код на C#
  • Код на Java
  • Код на Python 3
/** Файл main.cpp **************************/
#include <stdio.h>
#include "MyClasses.h"

/**
* Отображает информацию о переданном массиве фигур
*/
void ShowFigures(Figure *figures[], int size) {
    for(int i = 0; i < size; i++ ) {
        printf("Отображение %sа:\n", figures[i]->getName().c_str());
        figures[i]->ShowArea();
        figures[i]->ShowLength();
        figures[i]->ShowColor();
        printf("----------------------------\n\n");
    }
}

int main()
{
    Rectangle rectangle("прямоугольник", 2, 3, RED);
    Square square("квадрат", 5, GREEN);
    Circle circle("круг", 3, BLUE);
    
    int size = 3;
    Figure *figures[] = {&rectangle, &square, &circle};
    ShowFigures(figures, size);
        
    return 0;
}

/** Файл MyClasses.h **************************/
#include <map>
#include <string>
const int WHITE = 0xFFFFFF;
const int BLACK = 0x000000;
const int RED =   0xFF0000;
const int GREEN = 0x00FF00;
const int BLUE =  0x0000FF;


class Figure {
protected:
    int color;
    virtual double Area() = 0;
    virtual double Length() = 0;
private:
    std::map<int, std::string> colors;
    std::string name;
    void InitColorDict();
    std::string GetColorName();
    std::string GetRGBColor(); 
public:
    Figure(std::string name, int color);
    std::string getName();
    void setName(std::string name);
    void ShowArea();
    void ShowLength();
    void SetColor(int color);
    void ShowColor();
};

class Rectangle: public Figure {
protected:
    double Area() override;
    double Length() override;
private:
    int height,
        width;
public:
    Rectangle(std::string name);
    Rectangle(std::string name, int height, int width, int color);
};

class Square : public Rectangle {
public:
    Square(std::string name);
    Square(std::string name, int height, int color);
};

class Circle: public Figure {
protected:
    double Area() override;
    double Length() override;
private:
    int radius;
public:
    const double pi = 3.14;
    Circle(std::string name);
    Circle(std::string name, int radius, int color);
};

/** Файл MyClasses.cpp **************************/
#include <string>
#include <stdio.h>
#include <iostream>
#include <map>
#include "MyClasses.h"

Figure::Figure(std::string name,  int color) {
    setName(name);
    this->color = color;
    InitColorDict();
} 

std::string Figure::getName() {
    return this->name;
}

void Figure::setName(std::string name) {
    this->name = name;
}

/**
* Инициализирует список цветов
*/
void Figure::InitColorDict() {
    this->colors[WHITE] = "белый";
    this->colors[BLACK] = "чёрный";
    this->colors[RED] =   "красный";
    this->colors[GREEN] = "зелёный";
    this->colors[BLUE] =  "синий";
}

/**
* Возвращает наименование цвета фигуры
*/
std::string Figure::GetColorName() {
    return this->colors[this->color];
}

/**
* Возвращает RGBе цвета фигуры
*/        
std::string Figure::GetRGBColor() {
    char _color[8];
    sprintf(_color, "#%06X", color);

    return _color;
}

/**
* Отображает площадь
*/
void Figure::ShowArea() {
    printf("Площадь %s %s: %.2f\n", GetColorName().c_str(), getName().c_str(), Area());
}
        
/**
* Отображает длину периметра
*/
void Figure::ShowLength() {
    printf("Длина %s %s: %.2f\n", GetColorName().c_str(), getName().c_str(), Length());
}

/**
* Устанавливает цвет
*/
void Figure::SetColor(int color) {
    this->color = color;
}

/**
* Отображает цвет
*/
 void Figure::ShowColor() {
    printf("RGB цвет %s %s: %s\n", GetColorName().c_str(), getName().c_str(), GetRGBColor().c_str());
}

Rectangle::Rectangle(std::string name): Rectangle(name, 1, 1, BLACK) {}

Rectangle::Rectangle(std::string name, int height, int width, int color): Figure(name, color) {
    this->height = height;
    this->width = width;
}


double Rectangle::Area() {
    return height * width;
}

double Rectangle::Length() {
    return (height + width) * 2;
}

Square::Square(std::string name): Rectangle(name) {}

Square::Square(std::string name, int height, int color): Rectangle(name, height, height, color) {}

Circle::Circle(std::string name): Circle(name, 1, BLACK) {}

Circle::Circle(std::string name, int radius, int color): Figure(name, color) {
    this->radius = radius;
}

double Circle::Area() {
    return pi * (radius * radius);
}

double Circle::Length() {
    return 2 * pi * radius;
}
using System;
using System.Collections.Generic;

public class MainClass {
    const int WHITE = 0xFFFFFF;
    const int BLACK = 0x000000;
    const int RED =   0xFF0000;
    const int GREEN = 0x00FF00;
    const int BLUE =  0x0000FF;
    
    public static void Main() {
        Rectangle rectangle = new Rectangle("прямоугольник", 2, 3, RED);
        Square square = new Square("квадрат", 5, GREEN);
        Circle circle = new Circle("круг", 3, BLUE);
        
        Figure []figures = {rectangle, square, circle};
        ShowFigures(figures);
        
    }
    
    /**
    * Отображает информацию о переданном массиве фигур
    */
    static void ShowFigures(Figure[] figures) {
        foreach(Figure figure in figures) {
            Console.WriteLine("Отображение {0}а:", figure.Name);
            figure.ShowArea();
            figure.ShowLength();
            figure.ShowColor();
            Console.WriteLine("----------------------------\n");
        }
    }
    
    abstract class Figure {
        private Dictionary<int, string> colors;
        protected int color;
        
        /**
        * Конструктор
        */
        public Figure(string name, int color) {
            Name = name;
            this.color = color;
            InitColorDict();
        }
        
        public string Name { get; set; }
        
        /**
        * Инициализирует список цветов
        */
        private void InitColorDict() {
            colors = new Dictionary<int, string>();
            colors[WHITE] = "белый";
            colors[BLACK] = "чёрный";
            colors[RED] = "красный";
            colors[GREEN] = "зелёный";
            colors[BLUE] = "синий";
        }
        
        /** Возвращает площадь фигуры */
        abstract protected double Area();
        /** Возвращает длину периметра фигуры */
        abstract protected double Length();
        
        /**
        * Возвращает наименование цвета фигуры
        */
        private string GetColorName() {
            return colors[color];
        }
        
        /**
        * Возвращает RGBе цвета фигуры
        */        
        private string GetRGBColor() {
            return string.Format("#{0:X6}", color);
        }
        
        /**
        * Отображает площадь
        */
        public void ShowArea() {
            Console.WriteLine("Площадь {0} {1}: {2}", GetColorName(), Name, Area());
        }
        
        /**
        * Отображает длину периметра
        */
        public void ShowLength() {
            Console.WriteLine("Длина {0} {1}: {2}", GetColorName(), Name, Length());
        }
        
        /**
        * Устанавливает цвет
        */
        public void SetColor(int color) {
            this.color = color;
        }
        
        /**
        * Отображает цвет
        */
        public void ShowColor() {
            Console.WriteLine("RGB цвет {0} {1}: {2}", GetColorName(), Name, GetRGBColor());
        }
    }
    
    class Rectangle : Figure {
        private int height;
        private int width;
        
        public Rectangle(string name) : this(name, 1, 1, BLACK) {}
        
        public Rectangle(string name, int height, int width, int color) : base(name, color) {
            this.height = height;
            this.width = width;
        }
        
        override protected double Area() {
            return height * width;
        }
        
        override protected double Length() {
            return (height + width) * 2;
        }
        
    }

    class Square : Rectangle {
        public Square(string name) : base(name) {}
        
        public Square(string name, int height, int color) : base(name, height, height, color) {
        }
    }

    class Circle: Figure {
        const double pi =  3.14;
        private int radius;
        
        public Circle(string name) : this(name, 1, BLACK) {}
        
        public Circle(string name, int radius, int color): base(name, color) {
            this.radius = radius;
        }
        
        override protected double Area() {
            return pi * (radius * radius);
        }
        
        override protected double Length() {
            return 2 * pi * radius;
        }
    }
}
import java.util.HashMap;
public class Main {
    static final int WHITE = 0xFFFFFF;
    static final int BLACK = 0x000000;
    static final int RED =   0xFF0000;
    static final int GREEN = 0x00FF00;
    static final int BLUE =  0x0000FF;
    
    public static void main(String[] args) {
        Main main = new Main();
        main.Start();
    }
    
    public void Start() {
        Rectangle rectangle = new Rectangle("прямоугольник", 2, 3, RED);
        Square square = new Square("квадрат", 5, GREEN);
        Circle circle = new Circle("круг", 3, BLUE);
        
        Figure []figures = {rectangle, square, circle};
        ShowFigures(figures);
    }
    
        /**
    * Отображает информацию о переданном массиве фигур
    */
    static void ShowFigures(Figure[] figures) {
        for(Figure figure: figures) {
            System.out.printf("Отображение %sа:\n", figure.getName());
            figure.ShowArea();
            figure.ShowLength();
            figure.ShowColor();
            System.out.println("----------------------------\n");
        }
    }
    
    abstract class Figure {
        private HashMap<Integer, String> colors;
        private String name;
        protected int color;
        
        /**
        * Конструктор
        */
        public Figure(String name, int color) {
            setName(name);
            this.color = color;
            InitColorDict();
        }
        
        /**
        * Возвращает наименование фигуры
        */
        public String getName() { 
            return name;
        }
        
        /**
        * Устанавливает наименование фигуры
        */
        public void setName(String name) {
            this.name = name;
        }
        
        /**
        * Инициализирует список цветов
        */
        private void InitColorDict() {
            colors = new HashMap<Integer, String>();
            colors.put(WHITE, "белый");
            colors.put(BLACK, "чёрный");
            colors.put(RED, "красный");
            colors.put(GREEN, "зелёный");
            colors.put(BLUE, "синий");
        }
        
        /** Возвращает площадь фигуры */
        abstract protected double Area();
        /** Возвращает длину периметра фигуры */
        abstract protected double Length();
        
        /**
        * Возвращает наименование цвета фигуры
        */
        private String GetColorName() {
            return colors.get(color);
        }
        
        /**
        * Возвращает RGBе цвета фигуры
        */        
        private String GetRGBColor() {
            return String.format("#%06X", color);
        }
        
        /**
        * Отображает площадь
        */
        public void ShowArea() {
            System.out.printf("Площадь %s %s: %.2f\n", GetColorName(), getName(), Area());
        }
        
        /**
        * Отображает длину периметра
        */
        public void ShowLength() {
           System.out.printf("Длина %s %s: %.2f\n", GetColorName(), getName(), Length());
        }
        
        /**
        * Устанавливает цвет
        */
        public void SetColor(int color) {
            this.color = color;
        }
        
        /**
        * Отображает цвет
        */
        public void ShowColor() {
            System.out.printf("RGB цвет %s %s: %s\n", GetColorName(), getName(), GetRGBColor());
        }
    }
	
class Rectangle extends Figure {
        private int height;
        private int width;
        
        public Rectangle(String name) {
            this(name, 1, 1, BLACK);
        }
        
        public Rectangle(String name, int height, int width, int color) {
            super(name, color);
            this.height = height;
            this.width = width;
        }
        
        /** 
        * Возвращает площадь фигуры 
        */
        @Override
        protected double Area() {
            return height * width;
        }
        
        /** 
        * Возвращает длину периметра фигуры 
        */
        @Override
        protected double Length() {
            return (height + width) * 2;
        }
    }
    
    class Square extends Rectangle {
        public Square(String name) {
            super(name);
        }
        
        public Square(String name, int height, int color) {
            super(name, height, height, color);
        }
    }
    
    class Circle extends Figure {
        final double pi =  3.14;
        private int radius;
        
        public Circle(String name) {
            this(name, 1, BLACK);
        }
        
        public Circle(String name, int radius, int color) {
            super(name, color);
            this.radius = radius;
        }
        
        @Override
        protected double Area() {
            return pi * (radius * radius);
        }
        
        @Override
        protected double Length() {
            return 2 * pi * radius;
        }
    }
}
from abc import ABC, abstractmethod

WHITE = 0xFFFFFF
BLACK = 0x000000
RED = 0xFF0000
GREEN = 0x00FF00
BLUE = 0x0000FF


class Figure(ABC):
    def __init__(self, name: str, color: int):
        self._colors = dict()
        self._color = color
        self.name = name

        self._init_color_dict()

    def __str__(self):
        return self.name

    @property
    def name(self) -> str:
        return self._name

    @name.setter
    def name(self, name: str):
        self._name = name

    def _init_color_dict(self):
        """ Инициализирует список цвет """
        self._colors = {WHITE: 'белый',
                        BLACK: 'чёрный',
                        RED: 'красный',
                        GREEN: 'зелёный',
                        BLUE: 'синий'}

    @abstractmethod
    def _area(self) -> float:
        """ Возвращает площадь фигуры """
        raise NotImplementedError()

    @abstractmethod
    def _length(self) -> float:
        """ Возвращает длину периметра фигуры """
        raise NotImplementedError()

    def _get_color_name(self) -> str:
        """ Возвращает наименование цвета фигуры """
        return self._colors[self._color]

    def _get_rgb_color(self) -> str:
        """ Возвращает RGBе цвета фигуры """
        return "#{0:06X}".format(self._color)

    def show_area(self):
        """ Отображает площадь """
        print("Площадь {} {}: {}".format(self._get_color_name(), self.name, self._area()))

    def show_length(self):
        """ Отображает длину периметра """
        print("Длина {} {}: {}".format(self._get_color_name(), self.name, self._length()))

    def set_color(self, color: int):
        """ Устанавливает цвет """
        self._color = color

    def show_color(self):
        """ Отображает цвет """
        print("RGB цвет {} {}: {}".format(self._get_color_name(), self.name, self._get_rgb_color()))


class Rectangle(Figure):
    def __init__(self, name: str, height: int=1, width: int=1, color: int=BLACK):
        super().__init__(name, color)
        self._height = height
        self._width = width

    def _area(self) ->int:
        """ Возвращает площадь фигуры """
        return self._height * self._width

    def _length(self) -> int:
        """ Возвращает длину периметра фигуры """
        return (self._height + self._width) * 2


class Square(Rectangle):
    def __init__(self, name: str, height: int=1, color: int=BLACK):
        super().__init__(name, height, height, color)


class Circle(Figure):
    def __init__(self, name: str, radius: int=1, color: int=BLACK):
        super().__init__(name, color)
        self._pi = 3.14
        self._radius = radius

    def _area(self) -> float:
        """ Возвращает площадь фигуры """
        return self._pi * (self._radius * self._radius)

    def _length(self) -> float:
        """ Возвращает длину периметра фигуры """
        return 2 * self._pi * self._radius


def show_figures(figures: tuple):
    """ Отображает информацию о переданном массиве фигур """
    for figure in figures:
        if not isinstance(figure, Figure):
            raise TypeError('Переданный объект {} не является подклассом типа "Figure".'.format(figure))
        print("Отображение {}а".format(figure))
        figure.show_area()
        figure.show_length()
        figure.show_color()
        print("----------------------------\n")


def main():
    rectangle = Rectangle('прямоугольник', 2, 3, RED)
    square = Square('квадрат', 5, GREEN)
    circle = Circle('круг', 3, BLUE)

    figures = (rectangle, square, circle)
    show_figures(figures)


if __name__ == '__main__':
    main()

Результат работы:

Отображение прямоугольника:
Площадь красный прямоугольник: 6.00
Длина красный прямоугольник: 10.00
RGB цвет красный прямоугольник: #FF0000
----------------------------

Отображение квадрата:
Площадь зелёный квадрат: 25.00
Длина зелёный квадрат: 20.00
RGB цвет зелёный квадрат: #00FF00
----------------------------

Отображение круга:
Площадь синий круг: 28.26
Длина синий круг: 18.84
RGB цвет синий круг: #0000FF
----------------------------

Класс Figure имеет два абстрактных метода: double Area() и double Length(), которые, соответственно, должны рассчитывать и возвращать площадь и длину периметра фигуры. Но в данном классе эти методы абсолютно пустые и теперь уже имеют модификатор protected, то есть эти методы стало возможно изменить. Но, хотя они ещё и не реализованы, в открытых методах, которые отображают параметры фигуры, эти методы уже вовсю используются. Понятно, что без реализации этих абстрактных методов создание экземпляра класса не возможно. Поэтому и сам класс теперь имеет модификатор abstract, что означает, что класс Figure является абстрактным, и без реализации всех его абстрактных методов создать экземпляр такого класса невозможно.

А что возможно? Возможно, даже нужно, создать новый класс, наследовав его от класса Figure, и реализовать эти два недостающих, абстрактных метода, что и можно увидеть в программном коде выше. На этот раз класс Rectangle создан уже от класса Figure, и от него же создан и класс Circle. И так как площадь и длинна периметра у этих фигур рассчитываются по разному, то разумеется и абстрактные методы у классов реализованы по разному, в соответствии с фигурой, класс которой они представляют.

Так же в классе Figure была добавлена новая переменная string name, в которой должно храниться имя фигуры, и это имя, опять же, используется в открытых методах. Соответственно получается, что чтобы класс наследник работал корректно, эта переменная в нем при создании экземпляра класса обязательно должна быть определена, и поэтому, уже в конструкторе абстрактного класса, это предусмотрено. При создании классов наследников это надо обязательно учитывать и не забыть в конструкторе класса наследника вызвать конструктор базового класса, ведь мало ли какие переменные там определяются.

/** Конструктор абстрактного класса Фигура */
public Figure(string name, int color) {
    Name = name;
    this.color = color;
    InitColorDict();
}
/** Конструктор класса Прямоугольник */
public Rectangle(string name, int height, int width, int color): base(name, color) {
    this.height = height;
    this.width = width;
}
/** Конструктор класса Квадрат, наследованного уже от класса Прямоугольник */
public Square(string name, int height, int color): 
    base(name, height, height, color) {}
/** Конструктор класса Круг */
public Circle(string name, int radius, int color): base(name, color) {
    this.radius = radius;
}

Как видно, в нашем случае в конструкторе базового класса определяется не только переменная string name, но и переменная int color, и ещё посредством метода InitColorDict() инициализируется словарь цветов, позволяющий сконвертировать по шестнадцатеричному значению строковое наименование цвета. Если все эти переменные не будут определены при создании нового экземпляра класса, то очевидно, что использование этого экземпляра вызовет ошибку.

И так, теперь создано три класса унаследованных от базового класса Figure. Вернее непосредственно от этого класса создано два новых класса - Rectangle и Circle, а класс Square уже создан от класса прямоугольника Rectangle. И тем не менее класс Square с точки зрения ООП считается наследником не только для класса Rectangle, но и для класса Figure. И теперь у нас на конец-то появилась возможность создать массив, в который можно добавить вместе и прямоугольник и квадрат и круг, и передать весь этот массив в функцию на обработку.

    public static void Main() {
        // Создаём по экземпляру каждой фигуры, 
        // хотя могли бы создать и не по одному экземпляру, а больше,
        Rectangle rectangle = new Rectangle("прямоугольник", 2, 3, RED);
        Square square = new Square("квадрат", 5, GREEN);
        Circle circle = new Circle("круг", 3, BLUE);
        // всех их добавляем в один массив фигур,
        Figure []figures = {rectangle, square, circle};
        // и передаём на обработку в функцию.
        ShowFigures(figures);
    }
    
    /**
    * Эта функция отображает информацию о переданном массиве фигур
    */
    static void ShowFigures(Figure[] figures) {
        foreach(Figure figure in figures) {
            Console.WriteLine("Отображение {0}а:", figure.Name);
            figure.ShowArea();
            figure.ShowLength();
            figure.ShowColor();
            Console.WriteLine("----------------------------\n");
        }
    }

8. Интерфейс

В классе Figure присутствует открытый метод SetColor(int color), который способен установить цвет фигуры уже после создания экземпляра класса. Он позволяет, что-то вроде перекрасить фигуру. Так же как и отображение параметров фигур, перекраску фигур можно тоже автоматизировать, создав примерно вот такую процедуру,

static void Recolor(Figure[] figures) {
    foreach(Figure figure in figures) {
        figure.SetColor(WHITE);
    }
}

которая должна перекрасить все фигуры, из переданного массива фигур, в белый цвет. Но ведь не только фигуры имеют цвет, и не только они могут быть перекрашенными. А что, если бы у нас кроме фигур еще был бы, допустим, класс Буквы (Letter), который бы тоже требовал перекраски? Создавать еще одну процедуру, почти такую же как и показанную выше, но уже для букв? Но ведь это опять же будет нарушать принцип DRY. И тут мы вспоминаем, что существует замечательный принцип полиморфизма, а реализовать его можно воспользовавшись интерфейсом, как представлено ниже:

  • Код на C++
  • Код на C#
  • Код на Java
  • Код на Python 3
/** Файл main.cpp **************************/
#include <stdio.h>
#include "MyClasses.h"

/**
* Отображает информацию о переданном массиве фигур
*/
void ShowFigures(Figure *figures[], int size) {
   /* int n = sizeof(figures)/sizeof(figures[0]); */
    for(int i = 0; i < size; i++ ) {
        printf("Отображение %sа:\n", figures[i]->getName().c_str());
        figures[i]->ShowArea();
        figures[i]->ShowLength();
        figures[i]->ShowColor();
        printf("----------------------------\n\n");
    }
}

/**
* Перекрашивает цветные объекты из массива цветных объектов
*/    
void Recolor(IColor *colorObjects[], int size) {
    /* int n = sizeof(colorObjects)/sizeof(colorObjects[0]); */
    for(int i = 0; i < size; i++) {
        colorObjects[i]->SetColor(WHITE);
    }
}

int main()
{
    Rectangle rectangle("прямоугольник", 2, 3, RED);
    Square square("квадрат", 5, GREEN);
    Circle circle("круг", 3, BLUE);
    
    Figure *figures[] = {&rectangle, &square, &circle};
    ShowFigures(figures, 3);
    
    Letter letter('Z', 11, BLACK);
    letter.ShowLetterInfo();
    printf("----------------------------\n");
    IColor *colors[] = {&rectangle, &square, &circle, &letter};
    Recolor(colors, 4);
    printf("!!! После перекраски !!!\n");
    ShowFigures(figures, 3);
    letter.ShowLetterInfo();
    printf("----------------------------\n");
        
    return 0;
}

/** Файл MyClasses.h **************************/
#include <map>
#include <string>
const int WHITE = 0xFFFFFF;
const int BLACK = 0x000000;
const int RED =   0xFF0000;
const int GREEN = 0x00FF00;
const int BLUE =  0x0000FF;

/** Интерфейс цвета */
class IColor {
public:
    virtual void SetColor(int color) = 0;
};

class Figure: public IColor {
protected:
    int color;
    virtual double Area() = 0;
    virtual double Length() = 0;
private:
    std::map<int, std::string> colors;
    std::string name;
    void InitColorDict();
    std::string GetColorName();
    std::string GetRGBColor(); 
public:
    Figure(std::string name, int color);
    std::string getName();
    void setName(std::string name);
    void ShowArea();
    void ShowLength();
    void SetColor(int color) override;
    void ShowColor();
};

class Rectangle: public Figure {
protected:
    double Area() override;
    double Length() override;
private:
    int height,
        width;
public:
    Rectangle(std::string name);
    Rectangle(std::string name, int height, int width, int color);
};

class Square : public Rectangle {
public:
    Square(std::string name);
    Square(std::string name, int height, int color);
};

class Circle: public Figure {
protected:
    double Area() override;
    double Length() override;
private:
    int radius;
public:
    const double pi = 3.14;
    Circle(std::string name);
    Circle(std::string name, int radius, int color);
};

class Letter: public IColor {
private:
    char letter;
    int  size,
         color;
    bool isBold,
         isItalic;
public:
    Letter();
    Letter(char letter, int size, int color);
    bool getIsBold();
    void setIsBold(bool isBold);
    bool getIsItalic();
    void setIsItalic(bool isItalic);
    void SetColor(int color) override;
    void ShowLetterInfo();
};

/** Файл MyClasses.cpp **************************/
#include <string>
#include <stdio.h>
#include <iostream>
#include <map>
#include "MyClasses.h"

Figure::Figure(std::string name, int color) {
    setName(name);
    this->color = color;
    InitColorDict();
} 

std::string Figure::getName() {
    return this->name;
}

void Figure::setName(std::string name) {
    this->name = name;
}

/**
* Инициализирует список цветов
*/
void Figure::InitColorDict() {
    this->colors[WHITE] = "белый";
    this->colors[BLACK] = "чёрный";
    this->colors[RED] =   "красный";
    this->colors[GREEN] = "зелёный";
    this->colors[BLUE] =  "синий";
}

/**
* Возвращает наименование цвета фигуры
*/
std::string Figure::GetColorName() {
    return this->colors[this->color];
}

/**
* Возвращает RGBе цвета фигуры
*/        
std::string Figure::GetRGBColor() {
    char _color[8];
    sprintf(_color, "#%06X", color);

    return _color;
}

/**
* Отображает площадь
*/
void Figure::ShowArea() {
    printf("Площадь %s %s: %.2f\n", GetColorName().c_str(), getName().c_str(), Area());
}
        
/**
* Отображает длину периметра
*/
void Figure::ShowLength() {
    printf("Длина %s %s: %.2f\n", GetColorName().c_str(), getName().c_str(), Length());
}

/**
* Устанавливает цвет
*/
void Figure::SetColor(int color) {
    this->color = color;
}

/**
* Отображает цвет
*/
 void Figure::ShowColor() {
    printf("RGB цвет %s %s: %s\n", GetColorName().c_str(), getName().c_str(), GetRGBColor().c_str());
}

Rectangle::Rectangle(std::string name): Rectangle(name, 1, 1, BLACK) {}

Rectangle::Rectangle(std::string name, int height, int width, int color): Figure(name, color) {
    this->height = height;
    this->width = width;
}


double Rectangle::Area() {
    return height * width;
}

double Rectangle::Length() {
    return (height + width) * 2;
}

Square::Square(std::string name): Rectangle(name) {}

Square::Square(std::string name, int height, int color): Rectangle(name, height, height, color) {}

Circle::Circle(std::string name): Circle(name, 1, BLACK) {}

Circle::Circle(std::string name, int radius, int color): Figure(name, color) {
    this->radius = radius;}

double Circle::Area() {
    return pi * (radius * radius);
}

double Circle::Length() {
    return 2 * pi * radius;
}

Letter::Letter(): Letter('A', 10, BLACK) {}

Letter::Letter(char letter, int size, int color) {
    this->letter = letter;
    this->size = size;
    SetColor(color);
}

bool Letter::getIsBold() {
    return this->isBold;
}

void Letter::setIsBold(bool isBold) {
    this->isBold = isBold;
}

bool Letter::getIsItalic() {
    return this->isItalic;
}

void Letter::setIsItalic(bool isItalic) {
    this->isItalic = isItalic;
}

void Letter::SetColor(int color) {
    this->color = color;
}

void Letter::ShowLetterInfo() {
    printf("Буква: %c, размер: %d, RGB цвет: #%06X\n", letter, size, color);
}
using System;
using System.Collections.Generic;

public class MainClass {
    const int WHITE = 0xFFFFFF;
    const int BLACK = 0x000000;
    const int RED =   0xFF0000;
    const int GREEN = 0x00FF00;
    const int BLUE =  0x0000FF;
    
    public static void Main() {
        Rectangle rectangle = new Rectangle("прямоугольник", 2, 3, RED);
        Square square = new Square("квадрат", 5, GREEN);
        Circle circle = new Circle("круг", 3, BLUE);
        
        Figure []figures = {rectangle, square, circle};
        ShowFigures(figures);
        
        Letter letter = new Letter('Z', 11, BLACK);
        letter.ShowLetterInfo();
        Console.WriteLine("----------------------------\n");
        IColor []colorObjects = {rectangle, square, circle, letter};
        Recolor(colorObjects);
        Console.WriteLine("!!! После перекраски !!!");
        ShowFigures(figures);
        letter.ShowLetterInfo();
        Console.WriteLine("----------------------------\n");
    }
    
    /**
    * Отображает информацию о переданном массиве фигур
    */
    static void ShowFigures(Figure[] figures) {
        foreach(Figure figure in figures) {
            Console.WriteLine("Отображение {0}а:", figure.Name);
            figure.ShowArea();
            figure.ShowLength();
            figure.ShowColor();
            Console.WriteLine("----------------------------\n");
        }
    }
    
    /**
    * Перекрашивает цветные объекты из массива цветных объектов
    */    
    static void Recolor(IColor[] colorObjects) {
        foreach(IColor obj in colorObjects) {
            obj.SetColor(WHITE);
        }
    }
    
    /** Интерфейс цвета */
    public interface IColor {
        void SetColor(int color);
    }
    
    abstract class Figure: IColor {
        private Dictionary<int, string> colors;
        protected int color;
        
        /**
        * Конструктор
        */
        public Figure(string name, int color) {
            Name = name;
            this.color = color;
            InitColorDict();
        }
        
        public string Name { get; set; }
        
        /**
        * Инициализирует список цветов
        */
        private void InitColorDict() {
            colors = new Dictionary<int, string>();
            colors[WHITE] = "белый";
            colors[BLACK] = "чёрный";
            colors[RED] = "красный";
            colors[GREEN] = "зелёный";
            colors[BLUE] = "синий";
        }
        
        /** Возвращает площадь фигуры */
        abstract protected double Area();
        /** Возвращает длину периметра фигуры */
        abstract protected double Length();
        
        /**
        * Возвращает наименование цвета фигуры
        */
        private string GetColorName() {
            return colors[color];
        }
        
        /**
        * Возвращает RGBе цвета фигуры
        */        
        private string GetRGBColor() {
            return string.Format("#{0:X6}", color);
        }
        
        /**
        * Отображает площадь
        */
        public void ShowArea() {
            Console.WriteLine("Площадь {0} {1}: {2}", GetColorName(), Name, Area());
        }
        
        /**
        * Отображает длину периметра
        */
        public void ShowLength() {
            Console.WriteLine("Длина {0} {1}: {2}", GetColorName(), Name, Length());
        }
        
        /**
        * Устанавливает цвет
        */
        public void SetColor(int color) {
            this.color = color;
        }
        
        /**
        * Отображает цвет
        */
        public void ShowColor() {
            Console.WriteLine("RGB цвет {0} {1}: {2}", GetColorName(), Name, GetRGBColor());
        }
    }
    
    class Rectangle : Figure {
        private int height;
        private int width;
        
        public Rectangle(string name) : this(name, 1, 1, BLACK) {}
        
        public Rectangle(string name, int height, int width, int color) : base(name, color) {
            this.height = height;
            this.width = width;
        }
        
        override protected double Area() {
            return height * width;
        }
        
        override protected double Length() {
            return (height + width) * 2;
        }
        
    }

    class Square : Rectangle {
        public Square(string name) : base(name) {}
        
        public Square(string name, int height, int color) : base(name, height, height, color) {
        }
    }

    class Circle: Figure {
        const double pi =  3.14;
        private int radius;
        
        public Circle(string name) : this(name, 1, BLACK) {}
        
        public Circle(string name, int radius, int color): base(name, color) {
            this.radius = radius;
        }
        
        override protected double Area() {
            return pi * (radius * radius);
        }
        
        override protected double Length() {
            return 2 * pi * radius;
        }
    }
    
    class Letter : IColor {
        private char letter;
        private int size;
        private int color;
        
        public Letter() : this('A', 10, BLACK) {}
        
        public Letter(char letter, int size, int color) {
            this.letter = letter;
            this.size = size;
            SetColor(color);
            IsBold = false;
            IsItalic = false;
        }
        
        public bool IsBold { get; set;}
        public bool IsItalic { get; set; }
        
        /**
        * Устанавливает цвет
        */
        public void SetColor(int color) {
            this.color = color;
        }
        
        public void ShowLetterInfo() {
            Console.WriteLine("Буква: {0}, размер: {1}, RGB цвет: #{2:X6}", letter, size, color);
        }
    }
}
import java.util.HashMap;
public class Main {
    static final int WHITE = 0xFFFFFF;
    static final int BLACK = 0x000000;
    static final int RED =   0xFF0000;
    static final int GREEN = 0x00FF00;
    static final int BLUE =  0x0000FF;
    
    public static void main(String[] args) {
        Main main = new Main();
        main.Start();
    }
    
    public void Start() {
        Rectangle rectangle = new Rectangle("прямоугольник", 2, 3, RED);
        Square square = new Square("квадрат", 5, GREEN);
        Circle circle = new Circle("круг", 3, BLUE);
        
        Figure []figures = {rectangle, square, circle};
        ShowFigures(figures);
        
        Letter letter = new Letter('Z', 11, BLACK);
        letter.ShowLetterInfo();
        System.out.println("----------------------------\n");
        IColor []colorObjects = {rectangle, square, circle, letter};
        Recolor(colorObjects);
        System.out.println("!!! После перекраски !!!\n");
        ShowFigures(figures);
        letter.ShowLetterInfo();
        System.out.println("----------------------------\n");
    }
    
    /**
    * Отображает информацию о переданном массиве фигур
    */
    static void ShowFigures(Figure[] figures) {
        for(Figure figure: figures) {
            System.out.printf("Отображение %sа:\n", figure.getName());
            figure.ShowArea();
            figure.ShowLength();
            figure.ShowColor();
            System.out.println("----------------------------\n");
        }
    }
    
    /**
    * Перекрашивает цветные объекты из массива цветных объектов
    */    
    static void Recolor(IColor[] colorObjects) {
        for(IColor obj: colorObjects) {
            obj.SetColor(WHITE);
        }
    }
    
    /** Интерфейс цвета */
    public interface IColor {
        void SetColor(int color);
    }
    
    abstract class Figure implements IColor {
        private HashMap<Integer, String> colors;
        private String name;
        protected int color;
        
        /**
        * Конструктор
        */
        public Figure(String name, int color) {
            setName(name);
            this.color = color;
            InitColorDict();
        }
        
        /**
        * Возвращает наименование фигуры
        */
        public String getName() { 
            return name;
        }
        
        /**
        * Устанавливает наименование фигуры
        */
        public void setName(String name) {
            this.name = name;
        }
        
        /**
        * Инициализирует список цветов
        */
        private void InitColorDict() {
            colors = new HashMap<Integer, String>();
            colors.put(WHITE, "белый");
            colors.put(BLACK, "чёрный");
            colors.put(RED, "красный");
            colors.put(GREEN, "зелёный");
            colors.put(BLUE, "синий");
        }
        
        /** Возвращает площадь фигуры */
        abstract protected double Area();
        /** Возвращает длину периметра фигуры */
        abstract protected double Length();
        
        /**
        * Возвращает наименование цвета фигуры
        */
        private String GetColorName() {
            return colors.get(color);
        }
        
        /**
        * Возвращает RGBе цвета фигуры
        */        
        private String GetRGBColor() {
            return String.format("#%06X", color);
        }
        
        /**
        * Отображает площадь
        */
        public void ShowArea() {
            System.out.printf("Площадь %s %s: %.2f\n", GetColorName(), getName(), Area());
        }
        
        /**
        * Отображает длину периметра
        */
        public void ShowLength() {
           System.out.printf("Длина %s %s: %.2f\n", GetColorName(), getName(), Length());
        }
        
        /**
        * Устанавливает цвет
        */
        public void SetColor(int color) {
            this.color = color;
        }
        
        /**
        * Отображает цвет
        */
        public void ShowColor() {
            System.out.printf("RGB цвет %s %s: %s\n", GetColorName(), getName(), GetRGBColor());
        }
    }
	
	class Rectangle extends Figure {
        private int height;
        private int width;
        
        public Rectangle(String name) {
            this(name, 1, 1, BLACK);
        }
        
        public Rectangle(String name, int height, int width, int color) {
            super(name, color);
            this.height = height;
            this.width = width;
        }
        
        /** 
        * Возвращает площадь фигуры 
        */
        @Override
        protected double Area() {
            return height * width;
        }
        
        /** 
        * Возвращает длину периметра фигуры 
        */
        @Override
        protected double Length() {
            return (height + width) * 2;
        }
    }
    
    class Square extends Rectangle {
        public Square(String name) {
            super(name);
        }
        
        public Square(String name, int height, int color) {
            super(name, height, height, color);
        }
    }
    
    class Circle extends Figure {
        final double pi =  3.14;
        private int radius;
        
        public Circle(String name) {
            this(name, 1, BLACK);
        }
        
        public Circle(String name, int radius, int color) {
            super(name, color);
            this.radius = radius;
        }
        
        @Override
        protected double Area() {
            return pi * (radius * radius);
        }
        
        @Override
        protected double Length() {
            return 2 * pi * radius;
        }
    }
    
    class Letter implements IColor {
        private boolean isBold;
        private boolean isItalic;
        private char letter;
        private int size;
        private int color;
        
        public Letter() {
            this('A', 10, BLACK);
        } 
        
        public Letter(char letter, int size, int color) {
            this.letter = letter;
            this.size = size;
            SetColor(color);
            setIsBold(false);
            setIsItalic(false);
        }
        
        public boolean getIsBold() {
            return isBold;
        }
        
        public void setIsBold(boolean isBold) {
            this.isBold = isBold;
        }
        
        public boolean getIsItalic() {
            return isItalic;
        }
        
        public void setIsItalic(boolean isItalic) {
            this.isItalic = isItalic;
        }
        
        /**
        * Устанавливает цвет
        */
        public void SetColor(int color) {
            this.color = color;
        }
        
        public void ShowLetterInfo() {
            System.out.printf("Буква: %s, размер: %d, RGB цвет: #%06X\n", letter, size, color);
        }
    }
}
from abc import ABC, abstractmethod

WHITE = 0xFFFFFF
BLACK = 0x000000
RED =   0xFF0000
GREEN = 0x00FF00
BLUE =  0x0000FF


class IColor(ABC):
    """ Интерфейс цвета """
    @abstractmethod
    def set_color(self, color: int):
        raise NotImplementedError


class Figure(IColor, ABC):
    def __init__(self, name: str, color: int):
        self._colors = dict()
        self._color = color
        self.name = name

        self._init_color_dict()

    def __str__(self):
        return self.name

    @property
    def name(self) -> str:
        return self._name

    @name.setter
    def name(self, name: str):
        self._name = name

    def _init_color_dict(self):
        """ Инициализирует список цвет """
        self._colors = {WHITE: 'белый',
                        BLACK: 'чёрный',
                        RED: 'красный',
                        GREEN: 'зелёный',
                        BLUE: 'синий'}

    @abstractmethod
    def _area(self) -> float:
        """ Возвращает площадь фигуры """
        raise NotImplementedError()

    @abstractmethod
    def _length(self) -> float:
        """ Возвращает длину периметра фигуры """
        raise NotImplementedError()

    def _get_color_name(self) -> str:
        """ Возвращает наименование цвета фигуры """
        return self._colors[self._color]

    def _get_rgb_color(self) -> str:
        """ Возвращает RGBе цвета фигуры """
        return "#{0:06X}".format(self._color)

    def show_area(self):
        """ Отображает площадь """
        print("Площадь {} {}: {}".format(self._get_color_name(), self.name, self._area()))

    def show_length(self):
        """ Отображает длину периметра """
        print("Длина {} {}: {}".format(self._get_color_name(), self.name, self._length()))

    def set_color(self, color: int):
        """ Устанавливает цвет """
        self._color = color

    def show_color(self):
        """ Отображает цвет """
        print("RGB цвет {} {}: {}".format(self._get_color_name(), self.name, self._get_rgb_color()))


class Rectangle(Figure):
    def __init__(self, name: str, height: int=1, width: int=1, color: int=BLACK):
        super().__init__(name, color)
        self._height = height
        self._width = width
        self._color = color

    def _area(self) ->int:
        """ Возвращает площадь фигуры """
        return self._height * self._width

    def _length(self) -> int:
        """ Возвращает длину периметра фигуры """
        return (self._height + self._width) * 2


class Square(Rectangle):
    def __init__(self, name: str, height: int=1, color: int=BLACK):
        super().__init__(name, height, height, color)


class Circle(Figure):
    def __init__(self, name: str, radius: int=1, color: int=BLACK):
        super().__init__(name, color)
        self._pi = 3.14
        self._radius = radius
        self._color = color

    def _area(self) -> float:
        """ Возвращает площадь фигуры """
        return self._pi * (self._radius * self._radius)

    def _length(self) -> float:
        """ Возвращает длину периметра фигуры """
        return 2 * self._pi * self._radius


class Letter(IColor):
    def __init__(self, letter='A', size=10, color=BLACK):
        self._color = None
        self._letter = letter
        self._size = size
        self.set_color(color)
        self.is_bold = False
        self.is_italic = False

    @property
    def is_bold(self):
        return self._is_bold

    @is_bold.setter
    def is_bold(self, is_bold):
        self._is_bold = is_bold

    @property
    def is_italic(self):
        return self._is_italic

    @is_italic.setter
    def is_italic(self, is_italic):
        self._is_italic = is_italic

    def set_color(self, color: int):
        """ Устанавливает цвет """
        self._color = color

    def show_letter_info(self):
        print("Буква {0}, размер {1}, RGB цвет #{2:06X}".format(self._letter, self._size, self._color))


def show_figures(figures: tuple):
    """ Отображает информацию о переданном массиве фигур """
    for figure in figures:
        if not isinstance(figure, Figure):
            raise TypeError('Переданный объект {} не является подклассом типа "Figure".'.format(figure))
        print("Отображение {}а".format(figure))
        figure.show_area()
        figure.show_length()
        figure.show_color()
        print("----------------------------\n")


def recolor(color_objects: tuple):
    """ Перекрашивает цветные объекты из массива цветных объектов """
    for obj in color_objects:
        if not isinstance(obj, IColor):
            raise TypeError('Переданный объект {} не реализует интерфес "IColor".'.format(obj))
        obj.set_color(WHITE)


def main():
    rectangle = Rectangle('прямоугольник', 2, 3, RED)
    square = Square('квадрат', 5, GREEN)
    circle = Circle('круг', 3, BLUE)
    letter = Letter('Z', 11, BLACK)    

    figures = (rectangle, square, circle)
    show_figures(figures)
    letter.show_letter_info()
    print("----------------------------\n")

    color_objects = (rectangle, square, circle, letter)
    recolor(color_objects)
    print("!!! После перекраски !!!")
    show_figures(figures)
    letter.show_letter_info()
    print("----------------------------\n")


if __name__ == '__main__':
    main()

и результат работы:

Отображение прямоугольника:
Площадь красный прямоугольник: 6.00
Длина красный прямоугольник: 10.00
RGB цвет красный прямоугольник: #FF0000
----------------------------

Отображение квадрата:
Площадь зелёный квадрат: 25.00
Длина зелёный квадрат: 20.00
RGB цвет зелёный квадрат: #00FF00
----------------------------

Отображение круга:
Площадь синий круг: 28.26
Длина синий круг: 18.84
RGB цвет синий круг: #0000FF
----------------------------

Буква: Z, размер: 11, RGB цвет: #000000
----------------------------
!!! После перекраски !!!
Отображение прямоугольника:
Площадь белый прямоугольник: 6.00
Длина белый прямоугольник: 10.00
RGB цвет белый прямоугольник: #FFFFFF
----------------------------

Отображение квадрата:
Площадь белый квадрат: 25.00
Длина белый квадрат: 20.00
RGB цвет белый квадрат: #FFFFFF
----------------------------

Отображение круга:
Площадь белый круг: 28.26
Длина белый круг: 18.84
RGB цвет белый круг: #FFFFFF
----------------------------

Буква: Z, размер: 11, RGB цвет: #FFFFFF
----------------------------

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

public interface IColor {
    void SetColor(int color);
}

Также в некоторых языках программирования иногда еще присутствует понятие миксин (mixin), это когда уже к готовому классу можно ещё добавить какие-нибудь дополнительные, нужные методы. Эти методы формируются в отдельном классе-миксине, после чего этот миксин добавляется к классу как и интерфейс, только методы к нему добавляются уже реализованные. Миксины, например, используются в языке Python.

И так, теперь абстрактный класс Figure использует интерфейс IColor, в котором указано, что класс, использующий  данный интерфейс, должен обязательно реализовать метод SetColor(int color). И класс Letter также использует интерфейс IColor. И поэтому теперь стало возможным создать процедуру, которая смогла бы перекрашивать экземпляры от обоих этих классов:

/**
* Перекрашивает цветные объекты из массива цветных объектов
*/    
static void Recolor(IColor[] colorObjects) {
    foreach(IColor obj in colorObjects) {
        obj.SetColor(WHITE);
    }
}
public static void Main() {
    Rectangle rectangle = new Rectangle("прямоугольник", 2, 3, RED);
    Square square = new Square("квадрат", 5, GREEN);
    Circle circle = new Circle("круг", 3, BLUE);  
    Letter letter = new Letter('Z', 11, BLACK);

    IColor []colorObjects = {rectangle, square, circle, letter};
    Recolor(colorObjects);
}

Собственно говоря, процедуру перекрашивания Recolor(IColor[] colorObjects) можно использовать и для любого другого класса, разумеется если в нем используется цвет аналогично классам выше, и для этого к классу всего лишь надо добавить использование интерфейса IColor.

abstract class Figure: IColor { ..... }
class Letter: IColor { ...... }
class AnyOtherClass: IColor { ...... }

9. Переопределение методов

В классе Figure созданы два абстрактных метода, которые обязательно надо переопределить. Но бывают случаи, когда некоторым методам нужно просто предоставить доступ на их изменение. То есть эти методы должны быть всё же реализованы, но если программиста, который собирается использовать этот класс, вдруг не устраивает результат реализации метода, или в нём чего-то не хватает, или требуется запустить какой-то свой алгоритм внутри этого метода, то он может полностью его изменить, или просто дополнить, вызвав внутри метода в определённом месте уже своего программного кода, этот метод базового класса. Чтобы метод стал доступным к изменению, его надо отметить модификатором virtual. Ниже пример:

  • Код на C++
  • Код на C#
  • Код на Java
  • Код на Python 3
/** Файл main.cpp **************************/
#include <stdio.h>
#include "MyClasses.h"

/**
* Отображает информацию о переданном массиве фигур
*/
void ShowFigures(Figure *figures[], int size) {
    for(int i = 0; i < size; i++ ) {
        printf("Отображение %sа:\n", figures[i]->getName().c_str());
        figures[i]->ShowArea();
        figures[i]->ShowLength();
        figures[i]->ShowColor();
        printf("----------------------------\n\n");
    }
}

int main()
{
    Rectangle rectangle("прямоугольник", 2, 3, RED);
    Square square("квадрат", 5, GREEN);
    Circle circle("круг", 3, BLUE);
    
    int size = 3;
    Figure *figures[] = {&rectangle, &square, &circle};
    ShowFigures(figures, size);
        
    return 0;
}

/** Файл MyClasses.h **************************/
#include <map>
#include <string>
const int WHITE = 0xFFFFFF;
const int BLACK = 0x000000;
const int RED =   0xFF0000;
const int GREEN = 0x00FF00;
const int BLUE =  0x0000FF;

class Figure {
protected:
    int color;
    virtual double Area() = 0;
    virtual double Length() = 0;
private:
    std::map<int, std::string> colors;
    std::string name;
    void InitColorDict();
    std::string GetColorName();
    std::string GetRGBColor(); 
public:
    Figure(std::string name);
    std::string getName();
    void setName(std::string name);
    virtual void ShowArea();
    virtual void ShowLength();
    void SetColor(int color);
    virtual void ShowColor();
};

class Rectangle: public Figure {
protected:
    double Area() override;
    double Length() override;
private:
    int height,
        width;
public:
    Rectangle(std::string name);
    Rectangle(std::string name, int height, int width, int color);
    void ShowArea();
    void ShowLength();
};

class Square : public Rectangle {
public:
    Square(std::string name);
    Square(std::string name, int height, int color);
};

class Circle: public Figure {
protected:
    double Area() override;
    double Length() override;
private:
    int radius;
public:
    const double pi = 3.14;
    Circle(std::string name);
    Circle(std::string name, int radius, int color);
};

/** Файл MyClasses.cpp **************************/
#include <string>
#include <stdio.h>
#include <iostream>
#include <map>
#include "MyClasses.h"

Figure::Figure(std::string name) {
    setName(name);
    InitColorDict();
} 

std::string Figure::getName() {
    return this->name;
}

void Figure::setName(std::string name) {
    this->name = name;
}

/**
* Инициализирует список цветов
*/
void Figure::InitColorDict() {
    this->colors[WHITE] = "белый";
    this->colors[BLACK] = "чёрный";
    this->colors[RED] =   "красный";
    this->colors[GREEN] = "зелёный";
    this->colors[BLUE] =  "синий";
}

/**
* Возвращает наименование цвета фигуры
*/
std::string Figure::GetColorName() {
    return this->colors[this->color];
}

/**
* Возвращает RGBе цвета фигуры
*/        
std::string Figure::GetRGBColor() {
    char _color[8];
    sprintf(_color, "#%06X", color);

    return _color;
}

/**
* Отображает площадь
*/
void Figure::ShowArea() {
    printf("Площадь %s %s: %.2f\n", GetColorName().c_str(), getName().c_str(), Area());
}
        
/**
* Отображает длину периметра
*/
void Figure::ShowLength() {
    printf("Длина %s %s: %.2f\n", GetColorName().c_str(), getName().c_str(), Length());
}

/**
* Устанавливает цвет
*/
void Figure::SetColor(int color) {
    this->color = color;
}

/**
* Отображает цвет
*/
 void Figure::ShowColor() {
    printf("RGB цвет %s %s: %s\n", GetColorName().c_str(), getName().c_str(), GetRGBColor().c_str());
}

Rectangle::Rectangle(std::string name): Rectangle(name, 1, 1, BLACK) {}

Rectangle::Rectangle(std::string name, int height, int width, int color): Figure(name) {
    this->height = height;
    this->width = width;
    this->color = color;
}


double Rectangle::Area() {
    return height * width;
}

double Rectangle::Length() {
    return (height + width) * 2;
}

void Rectangle::ShowArea() {
    printf("Площадь %s: %.2f\n", getName().c_str(), Area());
}

void Rectangle::ShowLength() {
    printf("Именно длина периметра %sа. ", getName().c_str());
    Figure::ShowLength();
}

Square::Square(std::string name): Rectangle(name) {}

Square::Square(std::string name, int height, int color): Rectangle(name, height, height, color) {}

Circle::Circle(std::string name): Circle(name, 1, BLACK) {}

Circle::Circle(std::string name, int radius, int color): Figure(name) {
    this->radius = radius;
    this->color = color;
}

double Circle::Area() {
    return pi * (radius * radius);
}

double Circle::Length() {
    return 2 * pi * radius;
}
using System;
using System.Collections.Generic;

public class MainClass {
    const int WHITE = 0xFFFFFF;
    const int BLACK = 0x000000;
    const int RED =   0xFF0000;
    const int GREEN = 0x00FF00;
    const int BLUE =  0x0000FF;
    
    public static void Main() {
        Rectangle rectangle = new Rectangle("прямоугольник", 2, 3, RED);
        Square square = new Square("квадрат", 5, GREEN);
        Circle circle = new Circle("круг", 3, BLUE);
        
        Figure []figures = {rectangle, square, circle};
        ShowFigures(figures);
        
    }
    
    /**
    * Отображает информацию о переданном массиве фигур
    */
    static void ShowFigures(Figure[] figures) {
        foreach(Figure figure in figures) {
            Console.WriteLine("Отображение {0}а:", figure.Name);
            figure.ShowArea();
            figure.ShowLength();
            figure.ShowColor();
            Console.WriteLine("----------------------------\n");
        }
    }
    
    
    abstract class Figure {
        private Dictionary<int, string> colors;
        protected int color;
        
        /**
        * Конструктор
        */
        public Figure(string name) {
            Name = name;
            
            InitColorDict();
        }
        
        public string Name { get; set; }
        
        /**
        * Инициализирует список цветов
        */
        private void InitColorDict() {
            colors = new Dictionary<int, string>();
            colors[WHITE] = "белый";
            colors[BLACK] = "чёрный";
            colors[RED] = "красный";
            colors[GREEN] = "зелёный";
            colors[BLUE] = "синий";
        }
        
        /** Возвращает площадь фигуры */
        abstract protected double Area();
        /** Возвращает длину периметра фигуры */
        abstract protected double Length();
        
        /**
        * Возвращает наименование цвета фигуры
        */
        private string GetColorName() {
            return colors[color];
        }
        
        /**
        * Возвращает RGBе цвета фигуры
        */        
        private string GetRGBColor() {
            return string.Format("#{0:X6}", color);
        }
        
        /**
        * Отображает площадь
        */
        public virtual void ShowArea() {
            Console.WriteLine("Площадь {0} {1}: {2}", GetColorName(), Name, Area());
        }
        
        /**
        * Отображает длину периметра
        */
        public virtual void ShowLength() {
            Console.WriteLine("Длина {0} {1}: {2}", GetColorName(), Name, Length());
        }
        
        /**
        * Устанавливает цвет
        */
        public void SetColor(int color) {
            this.color = color;
        }
        
        /**
        * Отображает цвет
        */
        public virtual void ShowColor() {
            Console.WriteLine("RGB цвет {0} {1}: {2}", GetColorName(), Name, GetRGBColor());
        }
    }
    
    class Rectangle : Figure {
        private int height;
        private int width;
        
        public Rectangle(string name) : this(name, 1, 1, BLACK) {}
        
        public Rectangle(string name, int height, int width, int color) : base(name) {
            this.height = height;
            this.width = width;
            this.color = color;
        }
        
        override protected double Area() {
            return height * width;
        }
        
        override protected double Length() {
            return (height + width) * 2;
        }
        
        /**
        * Отображает площадь
        */
        override public void ShowArea() {
            Console.WriteLine("Площадь {0}: {1}", Name, Area());
        }
        
        /**
        * Отображает длину периметра
        */
        override public void ShowLength() {
            Console.Write("Именно длина периметра {0}а. ", Name);
            base.ShowLength();
        }
    }

    class Square : Rectangle {
        public Square(string name) : base(name) {}
        
        public Square(string name, int height, int color) : base(name, height, height, color) {
        }
    }

    class Circle: Figure {
        const double pi =  3.14;
        private int radius;
        
        public Circle(string name) : this(name, 1, BLACK) {}
        
        public Circle(string name, int radius, int color): base(name) {
            this.radius = radius;
            this.color = color;
        }
        
        override protected double Area() {
            return pi * (radius * radius);
        }
        
        override protected double Length() {
            return 2 * pi * radius;
        }
    }
}
import java.util.HashMap;
public class Main {
    static final int WHITE = 0xFFFFFF;
    static final int BLACK = 0x000000;
    static final int RED =   0xFF0000;
    static final int GREEN = 0x00FF00;
    static final int BLUE =  0x0000FF;
    
    public static void main(String[] args) {
        Main main = new Main();
        main.Start();
    }
    
    public void Start() {
        Rectangle rectangle = new Rectangle("прямоугольник", 2, 3, RED);
        Square square = new Square("квадрат", 5, GREEN);
        Circle circle = new Circle("круг", 3, BLUE);
        
        Figure []figures = {rectangle, square, circle};
        ShowFigures(figures);
    }
    
        /**
    * Отображает информацию о переданном массиве фигур
    */
    static void ShowFigures(Figure[] figures) {
        for(Figure figure: figures) {
            System.out.printf("Отображение %sа:\n", figure.getName());
            figure.ShowArea();
            figure.ShowLength();
            figure.ShowColor();
            System.out.println("----------------------------\n");
        }
    }
    
    
    abstract class Figure {
        private HashMap<Integer, String> colors;
        private String name;
        protected int color;
        
        /**
        * Конструктор
        */
        public Figure(String name) {
            setName(name);
            
            InitColorDict();
        }
        
        /**
        * Возвращает наименование фигуры
        */
        public String getName() { 
            return name;
        }
        
        /**
        * Устанавливает наименование фигуры
        */
        public void setName(String name) {
            this.name = name;
        }
        
        /**
        * Инициализирует список цветов
        */
        private void InitColorDict() {
            colors = new HashMap<Integer, String>();
            colors.put(WHITE, "белый");
            colors.put(BLACK, "чёрный");
            colors.put(RED, "красный");
            colors.put(GREEN, "зелёный");
            colors.put(BLUE, "синий");
        }
        
        /** Возвращает площадь фигуры */
        abstract protected double Area();
        /** Возвращает длину периметра фигуры */
        abstract protected double Length();
        
        /**
        * Возвращает наименование цвета фигуры
        */
        private String GetColorName() {
            return colors.get(color);
        }
        
        /**
        * Возвращает RGBе цвета фигуры
        */        
        private String GetRGBColor() {
            return String.format("#%06X", color);
        }
        
        /**
        * Отображает площадь
        */
        public void ShowArea() {
            System.out.printf("Площадь %s %s: %.2f\n", GetColorName(), getName(), Area());
        }
        
        /**
        * Отображает длину периметра
        */
        public void ShowLength() {
           System.out.printf("Длина %s %s: %.2f\n", GetColorName(), getName(), Length());
        }
        
        /**
        * Устанавливает цвет
        */
        public void SetColor(int color) {
            this.color = color;
        }
        
        /**
        * Отображает цвет
        */
        public void ShowColor() {
            System.out.printf("RGB цвет %s %s: %s\n", GetColorName(), getName(), GetRGBColor());
        }
    }
	
	class Rectangle extends Figure {
        private int height;
        private int width;
        
        public Rectangle(String name) {
            this(name, 1, 1, BLACK);
        }
        
        public Rectangle(String name, int height, int width, int color) {
            super(name);
            this.height = height;
            this.width = width;
            this.color = color;
        }
        
        /** 
        * Возвращает площадь фигуры 
        */
        @Override
        protected double Area() {
            return height * width;
        }
        
        /** 
        * Возвращает длину периметра фигуры 
        */
        @Override
        protected double Length() {
            return (height + width) * 2;
        }
        
        /**
        * Отображает площадь
        */
        @Override
        public void ShowArea() {
            System.out.printf("Площадь %s: %.2f\n", getName(), Area());
        }
        
        /**
        * Отображает длину периметра
        */
        @Override
        public void ShowLength() {
            System.out.printf("Именно длина периметра %sа. ", getName());
            super.ShowLength();
        }
    }
    
    class Square extends Rectangle {
        public Square(String name) {
            super(name);
        }
        
        public Square(String name, int height, int color) {
            super(name, height, height, color);
        }
    }
    
    class Circle extends Figure {
        final double pi =  3.14;
        private int radius;
        
        public Circle(String name) {
            this(name, 1, BLACK);
        }
        
        public Circle(String name, int radius, int color) {
            super(name);
            this.radius = radius;
            this.color = color;
        }
        
        @Override
        protected double Area() {
            return pi * (radius * radius);
        }
        
        @Override
        protected double Length() {
            return 2 * pi * radius;
        }
    }
}
from abc import ABC, abstractmethod


WHITE = 0xFFFFFF
BLACK = 0x000000
RED = 0xFF0000
GREEN = 0x00FF00
BLUE = 0x0000FF


class Figure( ABC):
    def __init__(self, name: str):
        self._colors = dict()
        self._color = None
        self.name = name

        self._init_color_dict()

    def __str__(self):
        return self.name

    @property
    def name(self) -> str:
        return self._name

    @name.setter
    def name(self, name: str):
        self._name = name

    def _init_color_dict(self):
        """ Инициализирует список цвет """
        self._colors = {WHITE: 'белый',
                        BLACK: 'чёрный',
                        RED: 'красный',
                        GREEN: 'зелёный',
                        BLUE: 'синий'}

    @abstractmethod
    def _area(self) -> float:
        """ Возвращает площадь фигуры """
        raise NotImplementedError()

    @abstractmethod
    def _length(self) -> float:
        """ Возвращает длину периметра фигуры """
        raise NotImplementedError()

    def _get_color_name(self) -> str:
        """ Возвращает наименование цвета фигуры """
        return self._colors[self._color]

    def _get_rgb_color(self) -> str:
        """ Возвращает RGBе цвета фигуры """
        return "#{0:06X}".format(self._color)

    def show_area(self):
        """ Отображает площадь """
        print("Площадь {} {}: {}".format(self._get_color_name(), self.name, self._area()))

    def show_length(self):
        """ Отображает длину периметра """
        print("Длина {} {}: {}".format(self._get_color_name(), self.name, self._length()))

    def set_color(self, color: int):
        """ Устанавливает цвет """
        self._color = color

    def show_color(self):
        """ Отображает цвет """
        print("RGB цвет {} {}: {}".format(self._get_color_name(), self.name, self._get_rgb_color()))


class Rectangle(Figure):
    def __init__(self, name: str, height: int=1, width: int=1, color: int=BLACK):
        super().__init__(name)
        self._height = height
        self._width = width
        self._color = color

    def _area(self) ->int:
        """ Возвращает площадь фигуры """
        return self._height * self._width

    def _length(self) -> int:
        """ Возвращает длину периметра фигуры """
        return (self._height + self._width) * 2

    def show_area(self):
        """ Отображает площадь """
        print("Площадь {}: {}".format(self.name, self._area()))

    def show_length(self):
        """ Отображает длину периметра """
        print("Именно длина периметра {}а.".format(self.name), end=' ')
        super().show_length()


class Square(Rectangle):
    def __init__(self, name: str, height: int=1, color: int=BLACK):
        super().__init__(name, height, height, color)


class Circle(Figure):
    def __init__(self, name: str, radius: int=1, color: int=BLACK):
        super().__init__(name)
        self._pi = 3.14
        self._radius = radius
        self._color = color

    def _area(self) -> float:
        """ Возвращает площадь фигуры """
        return self._pi * (self._radius * self._radius)

    def _length(self) -> float:
        """ Возвращает длину периметра фигуры """
        return 2 * self._pi * self._radius


def show_figures(figures: tuple):
    """ Отображает информацию о переданном массиве фигур """
    for figure in figures:
        if not isinstance(figure, Figure):
            raise TypeError('Переданный объект {} не является подклассом типа "Figure".'.format(figure))
        print("Отображение {}а".format(figure))
        figure.show_area()
        figure.show_length()
        figure.show_color()
        print("----------------------------\n")


def main():
    rectangle = Rectangle('прямоугольник', 2, 3, RED)
    square = Square('квадрат', 5, GREEN)
    circle = Circle('круг', 3, BLUE)

    figures = (rectangle, square, circle)
    show_figures(figures)


if __name__ == '__main__':
    main()

И результат его работы:

Отображение прямоугольника:
Площадь прямоугольник: 6.00
Именно длина периметра прямоугольника. Длина красный прямоугольник: 10.00
RGB цвет красный прямоугольник: #FF0000
----------------------------

Отображение квадрата:
Площадь квадрат: 25.00
Именно длина периметра квадрата. Длина зелёный квадрат: 20.00
RGB цвет зелёный квадрат: #00FF00
----------------------------

Отображение круга:
Площадь синий круг: 28.26
Длина синий круг: 18.84
RGB цвет синий круг: #0000FF
----------------------------

Теперь в абстрактном классе Figure трём методам предоставлен доступ на их переопределение:

/**
* Отображает площадь
*/
public virtual void ShowArea() {
    Console.WriteLine("Площадь {0} {1}: {2}", GetColorName(), Name, Area());
}

/**
* Отображает длину периметра
*/
public virtual void ShowLength() {
    Console.WriteLine("Длина {0} {1}: {2}", GetColorName(), Name, Length());
}

/**
* Отображает цвет
*/
public virtual void ShowColor() {
    Console.WriteLine("RGB цвет {0} {1}: {2}", GetColorName(), Name, GetRGBColor());
}

два из которых в классе Rectangle были слегка переопределены:

/**
* Отображает площадь
*/
override public void ShowArea() {
    Console.WriteLine("Площадь {0}: {1}", Name, Area());
}
        
/**
* Отображает длину периметра
*/
override public void ShowLength() {
    Console.Write("Именно длина периметра {0}а. ", Name);
    // Вызов метода базового класса
    base.ShowLength();
}

10. Статические методы и переменные

Что если захотелось учитывать, сколько всего фигур было создано? Для этих целей подойдёт статическая переменная, которая будет относиться ко всему классу целиком, а не к какому-то её конкретному экземпляру, и таким образом можно создать счётчик созданных экземпляров классов. А для отображения информации о количестве созданных фигур можно будет воспользоваться открытым статическим методом, который сможет получить доступ к статической переменной и отобразить её состояние.

Вот пример:

  • Код на C++
  • Код на C#
  • Код на Java
  • Код на Python 3
/** Файл main.cpp **************************/
#include <stdio.h>
#include "MyClasses.h"

/**
* Отображает информацию о переданном массиве фигур
*/
void ShowFigures(Figure *figures[], int size) {
    for(int i = 0; i < size; i++ ) {
        printf("Отображение %sа:\n", figures[i]->getName().c_str());
        figures[i]->ShowArea();
        figures[i]->ShowLength();
        figures[i]->ShowColor();
        printf("----------------------------\n\n");
    }
}

int main()
{
    Rectangle rectangle("прямоугольник", 2, 3, RED);
    Square square("квадрат", 5, GREEN);
    Circle circle("круг", 3, BLUE);
    printf("----------------------------\n");
    Circle::ShowCount();
    printf("----------------------------\n\n");
    
    Figure *figures[] = {&rectangle, &square, &circle};
    int numberFigures = sizeof(figures)/sizeof(figures[0]); 
    ShowFigures(figures, numberFigures);
    Figure::ShowCount();
        
    return 0;
}

/** Файл MyClasses.h **************************/
#include <map>
#include <string>
const int WHITE = 0xFFFFFF;
const int BLACK = 0x000000;
const int RED =   0xFF0000;
const int GREEN = 0x00FF00;
const int BLUE =  0x0000FF;

class Figure {
protected:
    int color;
    virtual double Area() = 0;
    virtual double Length() = 0;
private:
    static inline int count = 0; 
    std::map<int, std::string> colors;
    std::string name;
    void InitColorDict();
    std::string GetColorName();
    std::string GetRGBColor(); 
public:
    Figure(std::string name);
    static void ShowCount();
    static void ShowCount(std::string name);
    std::string getName();
    void setName(std::string name);
    virtual void ShowArea();
    virtual void ShowLength();
    void SetColor(int color);
    virtual void ShowColor();
};

class Rectangle: public Figure {
protected:
    double Area() override;
    double Length() override;
private:
    int height,
        width;
public:
    Rectangle(std::string name);
    Rectangle(std::string name, int height, int width, int color);
    void ShowArea();
    void ShowLength();
};

class Square : public Rectangle {
public:
    Square(std::string name);
    Square(std::string name, int height, int color);
};

class Circle: public Figure {
protected:
    double Area() override;
    double Length() override;
private:
    int radius;
public:
    const double pi = 3.14;
    Circle(std::string name);
    Circle(std::string name, int radius, int color);
};

/** Файл MyClasses.cpp **************************/
#include <string>
#include <stdio.h>
#include <iostream>
#include <map>
#include "MyClasses.h"

Figure::Figure(std::string name) {
    setName(name);
    this->count++;
    ShowCount(this->name);
    InitColorDict();
}

void Figure::ShowCount() {
    printf("Всего фигур %d.\n", count);
}

void Figure::ShowCount(std::string name) {
    printf("Создана фигура %s. Всего фигур %d.\n", name.c_str(), count);
}

std::string Figure::getName() {
    return this->name;
}

void Figure::setName(std::string name) {
    this->name = name;
}

/**
* Инициализирует список цветов
*/
void Figure::InitColorDict() {
    this->colors[WHITE] = "белый";
    this->colors[BLACK] = "чёрный";
    this->colors[RED] =   "красный";
    this->colors[GREEN] = "зелёный";
    this->colors[BLUE] =  "синий";
}

/**
* Возвращает наименование цвета фигуры
*/
std::string Figure::GetColorName() {
    return this->colors[this->color];
}

/**
* Возвращает RGBе цвета фигуры
*/        
std::string Figure::GetRGBColor() {
    char _color[8];
    sprintf(_color, "#%06X", color);

    return _color;
}

/**
* Отображает площадь
*/
void Figure::ShowArea() {
    printf("Площадь %s %s: %.2f\n", GetColorName().c_str(), getName().c_str(), Area());
}
        
/**
* Отображает длину периметра
*/
void Figure::ShowLength() {
    printf("Длина %s %s: %.2f\n", GetColorName().c_str(), getName().c_str(), Length());
}

/**
* Устанавливает цвет
*/
void Figure::SetColor(int color) {
    this->color = color;
}

/**
* Отображает цвет
*/
 void Figure::ShowColor() {
    printf("RGB цвет %s %s: %s\n", GetColorName().c_str(), getName().c_str(), GetRGBColor().c_str());
}

Rectangle::Rectangle(std::string name): Rectangle(name, 1, 1, BLACK) {}

Rectangle::Rectangle(std::string name, int height, int width, int color): Figure(name) {
    this->height = height;
    this->width = width;
    this->color = color;
}

double Rectangle::Area() {
    return height * width;
}

double Rectangle::Length() {
    return (height + width) * 2;
}

void Rectangle::ShowArea() {
    printf("Площадь %s: %.2f\n", getName().c_str(), Area());
}

void Rectangle::ShowLength() {
    printf("Именно длина периметра %sа. ", getName().c_str());
    Figure::ShowLength();
}

Square::Square(std::string name): Rectangle(name) {}

Square::Square(std::string name, int height, int color): Rectangle(name, height, height, color) {}

Circle::Circle(std::string name): Circle(name, 1, BLACK) {}

Circle::Circle(std::string name, int radius, int color): Figure(name) {
    this->radius = radius;
    this->color = color;
}

double Circle::Area() {
    return pi * (radius * radius);
}

double Circle::Length() {
    return 2 * pi * radius;
}
using System;
using System.Collections.Generic;

public class MainClass {
    const int WHITE = 0xFFFFFF;
    const int BLACK = 0x000000;
    const int RED =   0xFF0000;
    const int GREEN = 0x00FF00;
    const int BLUE =  0x0000FF;
    
    public static void Main() {
        Figure.ShowCount();
        Rectangle rectangle = new Rectangle("прямоугольник", 2, 3, RED);
        Square square = new Square("квадрат", 5, GREEN);
        Circle circle = new Circle("круг", 3, BLUE);
        Console.WriteLine("----------------------------");
        Circle.ShowCount();
        Console.WriteLine("----------------------------\n");
        
        Figure []figures = {rectangle, square, circle};
        ShowFigures(figures);
        Figure.ShowCount();
    }
    
    /**
    * Отображает информацию о переданном массиве фигур
    */
    static void ShowFigures(Figure[] figures) {
        foreach(Figure figure in figures) {
            Console.WriteLine("Отображение {0}а:", figure.Name);
            figure.ShowArea();
            figure.ShowLength();
            figure.ShowColor();
            Console.WriteLine("----------------------------\n");
        }
    }
    
    abstract class Figure {
        static private int count = 0;
        private Dictionary<int, string> colors;
        protected int color;
        
        /**
        * Конструктор
        */
        public Figure(string name) {
            Name = name;
            count++;
            ShowCount(Name);
            InitColorDict();
        }
        
        static public void ShowCount(string name="") {
            if (name == "")
                Console.WriteLine("Всего фигур {0}.", count);
            else
                Console.WriteLine("Создана фигура {0}. Всего фигур {1}.", name, count);
        }
        
        public string Name { get; set; }
        
        /**
        * Инициализирует список цветов
        */
        private void InitColorDict() {
            colors = new Dictionary<int, string>();
            colors[WHITE] = "белый";
            colors[BLACK] = "чёрный";
            colors[RED] = "красный";
            colors[GREEN] = "зелёный";
            colors[BLUE] = "синий";
        }
        
        /** Возвращает площадь фигуры */
        abstract protected double Area();
        /** Возвращает длину периметра фигуры */
        abstract protected double Length();
        
        /**
        * Возвращает наименование цвета фигуры
        */
        private string GetColorName() {
            return colors[color];
        }
        
        /**
        * Возвращает RGBе цвета фигуры
        */        
        private string GetRGBColor() {
            return string.Format("#{0:X6}", color);
        }
        
        /**
        * Отображает площадь
        */
        public virtual void ShowArea() {
            Console.WriteLine("Площадь {0} {1}: {2}", GetColorName(), Name, Area());
        }
        
        /**
        * Отображает длину периметра
        */
        public virtual void ShowLength() {
            Console.WriteLine("Длина {0} {1}: {2}", GetColorName(), Name, Length());
        }
        
        /**
        * Устанавливает цвет
        */
        public void SetColor(int color) {
            this.color = color;
        }
        
        /**
        * Отображает цвет
        */
        public virtual void ShowColor() {
            Console.WriteLine("RGB цвет {0} {1}: {2}", GetColorName(), Name, GetRGBColor());
        }
    }
    
    class Rectangle : Figure {
        private int height;
        private int width;
        
        public Rectangle(string name) : this(name, 1, 1, BLACK) {}
        
        public Rectangle(string name, int height, int width, int color) : base(name) {
            this.height = height;
            this.width = width;
            this.color = color;
        }
        
        override protected double Area() {
            return height * width;
        }
        
        override protected double Length() {
            return (height + width) * 2;
        }
        
        /**
        * Отображает площадь
        */
        override public void ShowArea() {
            Console.WriteLine("Площадь {0}: {1}", Name, Area());
        }
        
        /**
        * Отображает длину периметра
        */
        override public void ShowLength() {
            Console.Write("Именно длина периметра {0}а. ", Name);
            base.ShowLength();
        }
    }

    class Square : Rectangle {
        public Square(string name) : base(name) {}
        
        public Square(string name, int height, int color) : base(name, height, height, color) {
        }
    }

    class Circle: Figure {
        const double pi =  3.14;
        private int radius;
        
        public Circle(string name) : this(name, 1, BLACK) {}
        
        public Circle(string name, int radius, int color): base(name) {
            this.radius = radius;
            this.color = color;
        }
        
        override protected double Area() {
            return pi * (radius * radius);
        }
        
        override protected double Length() {
            return 2 * pi * radius;
        }
    }
}
import java.util.HashMap;
public class Main {
    static final int WHITE = 0xFFFFFF;
    static final int BLACK = 0x000000;
    static final int RED =   0xFF0000;
    static final int GREEN = 0x00FF00;
    static final int BLUE =  0x0000FF;
    
    public static void main(String[] args) {
        Main main = new Main();
        main.Start();
    }
    
    public void Start() {
        Figure.ShowCount();
        Rectangle rectangle = new Rectangle("прямоугольник", 2, 3, RED);
        Square square = new Square("квадрат", 5, GREEN);
        Circle circle = new Circle("круг", 3, BLUE);
        System.out.println("----------------------------");
        Circle.ShowCount();
        System.out.println("----------------------------\n");
        
        Figure []figures = {rectangle, square, circle};
        ShowFigures(figures);
        Figure.ShowCount();
    }
    
        /**
    * Отображает информацию о переданном массиве фигур
    */
    static void ShowFigures(Figure[] figures) {
        for(Figure figure: figures) {
            System.out.printf("Отображение %sа:\n", figure.getName());
            figure.ShowArea();
            figure.ShowLength();
            figure.ShowColor();
            System.out.println("----------------------------\n");
        }
    }
    
    abstract static class Figure {
        static private int count = 0;
        private HashMap<Integer, String> colors;
        private String name;
        protected int color;
        
        /**
        * Конструктор
        */
        public Figure(String name) {
            setName(name);
            count++;
            ShowCount(getName());
            InitColorDict();
        }
        
        static public void ShowCount() {
            System.out.printf("Всего фигур %d.\n", count);
        }
        
        static public void ShowCount(String name) {
            System.out.printf("Создана фигура %s. Всего фигур %d.\n", name, count);
        }
        
        /**
        * Возвращает наименование фигуры
        */
        public String getName() { 
            return name;
        }
        
        /**
        * Устанавливает наименование фигуры
        */
        public void setName(String name) {
            this.name = name;
        }
        
        /**
        * Инициализирует список цветов
        */
        private void InitColorDict() {
            colors = new HashMap<Integer, String>();
            colors.put(WHITE, "белый");
            colors.put(BLACK, "чёрный");
            colors.put(RED, "красный");
            colors.put(GREEN, "зелёный");
            colors.put(BLUE, "синий");
        }
        
        /** Возвращает площадь фигуры */
        abstract protected double Area();
        /** Возвращает длину периметра фигуры */
        abstract protected double Length();
        
        /**
        * Возвращает наименование цвета фигуры
        */
        private String GetColorName() {
            return colors.get(color);
        }
        
        /**
        * Возвращает RGBе цвета фигуры
        */        
        private String GetRGBColor() {
            return String.format("#%06X", color);
        }
        
        /**
        * Отображает площадь
        */
        public void ShowArea() {
            System.out.printf("Площадь %s %s: %.2f\n", GetColorName(), getName(), Area());
        }
        
        /**
        * Отображает длину периметра
        */
        public void ShowLength() {
           System.out.printf("Длина %s %s: %.2f\n", GetColorName(), getName(), Length());
        }
        
        /**
        * Устанавливает цвет
        */
        public void SetColor(int color) {
            this.color = color;
        }
        
        /**
        * Отображает цвет
        */
        public void ShowColor() {
            System.out.printf("RGB цвет %s %s: %s\n", GetColorName(), getName(), GetRGBColor());
        }
    }
	
	class Rectangle extends Figure {
        private int height;
        private int width;
        
        public Rectangle(String name) {
            this(name, 1, 1, BLACK);
        }
        
        public Rectangle(String name, int height, int width, int color) {
            super(name);
            this.height = height;
            this.width = width;
            this.color = color;
        }
        
        /** 
        * Возвращает площадь фигуры 
        */
        @Override
        protected double Area() {
            return height * width;
        }
        
        /** 
        * Возвращает длину периметра фигуры 
        */
        @Override
        protected double Length() {
            return (height + width) * 2;
        }
        
        /**
        * Отображает площадь
        */
        @Override
        public void ShowArea() {
            System.out.printf("Площадь %s: %.2f\n", getName(), Area());
        }
        
        /**
        * Отображает длину периметра
        */
        @Override
        public void ShowLength() {
            System.out.printf("Именно длина периметра %sа. ", getName());
            super.ShowLength();
        }
    }
    
    class Square extends Rectangle {
        public Square(String name) {
            super(name);
        }
        
        public Square(String name, int height, int color) {
            super(name, height, height, color);
        }
    }
    
    class Circle extends Figure {
        final double pi =  3.14;
        private int radius;
        
        public Circle(String name) {
            this(name, 1, BLACK);
        }
        
        public Circle(String name, int radius, int color) {
            super(name);
            this.radius = radius;
            this.color = color;
        }
        
        @Override
        protected double Area() {
            return pi * (radius * radius);
        }
        
        @Override
        protected double Length() {
            return 2 * pi * radius;
        }
    }
}
from abc import ABC, abstractmethod


WHITE = 0xFFFFFF
BLACK = 0x000000
RED = 0xFF0000
GREEN = 0x00FF00
BLUE = 0x0000FF


class Figure(ABC):
    count = 0

    def __init__(self, name: str):
        self._colors = dict()
        self._color = None
        self.name = name
        Figure.count += 1
        self.show_count(name)
        self._init_color_dict()

    def __str__(self):
        return self.name

    @classmethod
    def show_count(cls, name=""):
        if name == "":
            print("Всего фигур {}.".format(cls.count))
        else:
            print("Создана фигура {}. Всего фигур {}".format(name, cls.count))

    @property
    def name(self) -> str:
        return self._name

    @name.setter
    def name(self, name: str):
        self._name = name

    def _init_color_dict(self):
        """ Инициализирует список цвет """
        self._colors = {WHITE: 'белый',
                        BLACK: 'чёрный',
                        RED: 'красный',
                        GREEN: 'зелёный',
                        BLUE: 'синий'}

    @abstractmethod
    def _area(self) -> float:
        """ Возвращает площадь фигуры """
        raise NotImplementedError()

    @abstractmethod
    def _length(self) -> float:
        """ Возвращает длину периметра фигуры """
        raise NotImplementedError()

    def _get_color_name(self) -> str:
        """ Возвращает наименование цвета фигуры """
        return self._colors[self._color]

    def _get_rgb_color(self) -> str:
        """ Возвращает RGBе цвета фигуры """
        return "#{0:06X}".format(self._color)

    def show_area(self):
        """ Отображает площадь """
        print("Площадь {} {}: {}".format(self._get_color_name(), self.name, self._area()))

    def show_length(self):
        """ Отображает длину периметра """
        print("Длина {} {}: {}".format(self._get_color_name(), self.name, self._length()))

    def set_color(self, color: int):
        """ Устанавливает цвет """
        self._color = color

    def show_color(self):
        """ Отображает цвет """
        print("RGB цвет {} {}: {}".format(self._get_color_name(), self.name, self._get_rgb_color()))


class Rectangle(Figure):
    def __init__(self, name: str, height: int=1, width: int=1, color: int=BLACK):
        super().__init__(name)
        self._height = height
        self._width = width
        self._color = color

    def _area(self) ->int:
        """ Возвращает площадь фигуры """
        return self._height * self._width

    def _length(self) -> int:
        """ Возвращает длину периметра фигуры """
        return (self._height + self._width) * 2

    def show_area(self):
        """ Отображает площадь """
        print("Площадь {}: {}".format(self.name, self._area()))

    def show_length(self):
        """ Отображает длину периметра """
        print("Именно длина периметра {}а.".format(self.name), end=' ')
        super().show_length()


class Square(Rectangle):
    def __init__(self, name: str, height: int=1, color: int=BLACK):
        super().__init__(name, height, height, color)


class Circle(Figure):
    def __init__(self, name: str, radius: int=1, color: int=BLACK):
        super().__init__(name)
        self._pi = 3.14
        self._radius = radius
        self._color = color

    def _area(self) -> float:
        """ Возвращает площадь фигуры """
        return self._pi * (self._radius * self._radius)

    def _length(self) -> float:
        """ Возвращает длину периметра фигуры """
        return 2 * self._pi * self._radius


def show_figures(figures: tuple):
    """ Отображает информацию о переданном массиве фигур """
    for figure in figures:
        if not isinstance(figure, Figure):
            raise TypeError('Переданный объект {} не является подклассом типа "Figure".'.format(figure))
        print("Отображение {}а".format(figure))
        figure.show_area()
        figure.show_length()
        figure.show_color()
        print("----------------------------\n")


def main():
    Figure.show_count()
    rectangle = Rectangle('прямоугольник', 2, 3, RED)
    square = Square('квадрат', 5, GREEN)
    circle = Circle('круг', 3, BLUE)
    print("----------------------------")
    Circle.show_count()
    print("----------------------------\n")

    figures = (rectangle, square, circle)
    show_figures(figures)
    Figure.show_count()


if __name__ == '__main__':
    main()

И результат его работы:

Создана фигура прямоугольник. Всего фигур 1.
Создана фигура квадрат. Всего фигур 2.
Создана фигура круг. Всего фигур 3.
----------------------------
Всего фигур 3.
----------------------------

Отображение прямоугольника:
Площадь прямоугольник: 6.00
Именно длина периметра прямоугольника. Длина красный прямоугольник: 10.00
RGB цвет красный прямоугольник: #FF0000
----------------------------

Отображение квадрата:
Площадь квадрат: 25.00
Именно длина периметра квадрата. Длина зелёный квадрат: 20.00
RGB цвет зелёный квадрат: #00FF00
----------------------------

Отображение круга:
Площадь синий круг: 28.26
Длина синий круг: 18.84
RGB цвет синий круг: #0000FF
----------------------------

Всего фигур 3.

Как можно увидеть, в класс Figure добавлена закрытая статическая переменная int count, в которой будет храниться количество экземпляров класса, и статический метод ShowCount(), который отображает состояние этой переменной, то есть количество созданных экземпляров. Чтобы это количество учитывать, в конструкторе класса, который автоматически запускается каждый раз, когда создаётся очередной новый экземпляр, переменная int count увеличивается на единицу, и так как инкрементация производиться именно в конструкторе базового класса Figure, то не важно экземпляр которого из классов наследников создаётся, количество всё равно будет увеличиваться. Таким образом, переменная int count будет хранить количество всех созданных фигур, и получить это количество можно из любого класса, даже базового:

abstract class Figure {
    static private int count = 0;
    .....
    static public void ShowCount(string name="") {
        if (name == "")
            Console.WriteLine("Всего фигур {0}.", count);
        else
            Console.WriteLine("Создана фигура {0}. Всего фигур {1}.", name, count);
    }
}
public static void Main() {
    // Вызов статического метода.
    Figure.ShowCount();
    Rectangle rectangle = new Rectangle("прямоугольник", 2, 3, RED);
    Square square = new Square("квадрат", 5, GREEN);
    Circle circle = new Circle("круг", 3, BLUE);
    Console.WriteLine("----------------------------");
    // Вызов статического метода.
    Circle.ShowCount();
    Console.WriteLine("----------------------------\n");
    
    Figure []figures = {rectangle, square, circle};
    ShowFigures(figures);
    // Вызов статического метода.
    Figure.ShowCount();
}

11. В заключении

Объектно-ориентированное программирование уже давно стало обязательным атрибутом почти всех языков программирования, которое значительно упрощает создание программного кода. Поэтому для любого программиста знание основ ООП обязательно, если Вы конечно не собираетесь специализироваться исключительно на ассемблере, или каком-нибудь декларативном языке программирования типа Prolog.

Но, если основы ООП в принципе во всех языках программирования довольно схожи, то особенности её реализации в различных языках как правило различаются, причём часто довольно сильно различаются. Например, в C# и Python в дополнении к классам были реализованы свойства, тогда как в Java и C++ их нет, там используются геттеры и сеттеры. Зато C++ и Python поддерживает множественное наследование классов, а в C# и Java возможно только множественное наследование интерфейсов. Кстати, так как вместе с множественным наследованием классов в дополнении придаётся множество неприятных заморочек (почему в C# и Java собственно от этого и отказались), то в Python чаще используются миксины (Mixin), которые конечно очень похоже на обычные классы, но все-таки имеют несколько другое назначение. Ещё в Python существуют не только классические статические методы, но и методы класса (не путать с методами экземпляра класса, которые чаше всего и используются), хотя они и весьма похожи. Так же в Python существует понятие не только классов, но и метаклассов, назначение которых что-то вроде создавать классы на лету. А в JavaScript в классах нельзя создать открытую переменную, там для этих целей надо использовать геттеры и сеттеры, которые фактически создают свойство, а переменные там исключительно локальные. Правда те же открытые переменные никто не мешает добавить классу уже после создания его экземпляра (да, в JavaScript и Python переменные и методы возможно добавлять в класс уже после его объявления). А вот методы в JavaScript (да и в Python тоже) напротив как раз-таки исключительно открытые, и закрытые методы обозначаются чисто условно нижним подчеркиванием в начале наименования метода.

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

Ну а когда Вы уже довольно уверенно освоите объектно-ориентированное программирование, то добро пожаловать на более высокий уровень - концептуальное программирование. И тут в Ваше распоряжение будет предоставлена модель MVC - Model-View-Controller (Модель-Представление-Контроллер) - это одна из моделей разделения логики при создании приложения. Такая модель стала очень популярной в среде WEB-framework для написания WEB-сайтов, наверное она действительно очень подходит для этой области программирования. Хотя, надо сказать, что я и при написании настольных приложений тоже по возможность всегда стараюсь придерживаться этой модели, в дальнейшем, при обслуживании программного кода, это очень облегчает жизнь. Но это уже совсем другая история.

к началу статьи
0 681 0
Мы используем cookie-файлы, чтобы получить статистику, которая помогает нам улучшить сервис для Вас с целью персонализации сервисов и предложений. Вы можете прочитать подробнее о cookie-файлах или изменить настройки браузера. Продолжая пользоваться сайтом без изменения настроек, вы даёте согласие на использование ваших cookie-файлов.