- Регистрация
- 13.04.2019
- Сообщения
- 800
- Благодарностей
- 547
- Баллы
- 93
Всех приветствую, на связи Brabus.
В этой статье я хотел бы продолжить цепочку статей по изучению языка программирования c# в рамках Zennoposter.
Если вы не читали мою предыдущую конкурсную статью об изучении c#, которая заняла 3е место, то советую сперва прочитать её:
Начинаем кодить на c#
В этой статье я расскажу вам о том, что такое ООП и зачем оно нужно. Данный материал, на мой взляд, вышел за рамки обычной статьи и превратился в полноценное руководство по азам ООП. Как следствие - материала получилось много.
У данной статьи есть минималный порог входа:
1) Нужно знать азы c#, которые я описал в первой статье по c#
2) Нужно уметь писать базовые шаблоны на Zennoposter
Если вы не усвоили минималный порог, то понять данную статью вам будет не просто)
ООП - объектно-ориентированное программирование. Из самого определения нам становится понятно, что данная методология программирования направлена на представление кода в виде объектов (сущностей), которые по заданной нами логике взаимодействуют с кодом и друг с другом.
Если вы когда-нибудь задумаетесь о карьере программиста и попадёте на собеседование на junior позицию, то первое, что вас спросят - это ООП и его принципы. Естественно, в этом примере я говорю о программировании на си-подобных языках с поддержкой ООП.
Я не зря в предыдущей статье о c# проговорил, что она посвящена ПРОЦЕДУРНОМУ подходу к программированию. Чтобы понять смысл ООП, нам сперва нужно вспомнить, что же такое процедурное программирование.
При процедурном программировании программа ожидает на входе какие-то данные, делает с ними то, что мы накодили и выдаёт результат.
Другими словами - код программы выполняется снизу вверх. От первой строчки кода банально вниз, до последней строчки кода.
Такой принцип программирования "актуален" до тех пор, пока код вашей программы умещается в условные 400-500 строк кода. Если ваша программа подразумевает постоянное масштабирование, обновление функционала и т.п, то спустя некоторое время вы обнаружите, что поддерживать такую программу становится всё сложнее и сложнее.
На помощь программистам пришло ООП. Оно позволило раздувать функционал прогаммы до любого обозримого размера и делать этот функционал вполне себе обслуживаемым и масштабируемым.
В рамках данной статьи мы начнем погружение в ООП с азов. Как мы помним, фундамент ООП - это какие-то объекты. Что за объекты, собственно говоря?
Объект в ООП - это сущность, которая имеет какие-то свойства, имеет какое-то состояние и реализует какие-то методы (функции). Чтобы ответить на этот вопрос точнее - нужно немного углубиться.
Класс в ООП - это некий шаблон или модель, по которому создаются объекты.
Я профи в рассылках, поэтому давайте разберем это на примере моих любимых вебмейлинг-рассылок. Все знают, что существует множество почтовиков, но мы возьмем 3 самых крупных в СНГ - mail, gmail и yandex. Любой из нас в любое время может создать почту в любом из этих почтовиков.
Будут ли отличатся наши аккаунты по функционалу или внешнему виду в рамках одного почтовика? - Нет, не будут. Любой аккаунт, ваш или соседа, будет иметь одни и те же поля для заполнения (ФИО, возраст, ГЕО, логин, пароль и т.п.), а также будет иметь один и тот же набор функционала (отправить письмо, получить письмо, удалить письмо и т.п.).
Будут ли отличатся между собой аккаунты разных почтовиков? - Конечно будут, хотя у них и есть похожий функционал (отправка письма, чтения письма и т.п.). У того же аккаунта gmail будет доступ к ютубу, у аккаунта yandex будет доступ к dzen, а у аккаунта mail будет доступ к Моему Миру.
Делаем вывод, что в этом примере mail, gmail и yandex - это классы, или другими словами - шаблоны, по которому создаются объекты, т.е. аккаунты. В каждом классе зашита своя логика, на основе которой создаются объекты.
Объекты взаимодействуют с кодом нашей программы. Также объекты разных классов могут взаимодействовать друг с другом. Аккаунт mail может отправить письмо на аккаунт gmail. Аккаунт yandex может удалить письмо от mail.
Давайте для примера создадим классы Mail, Gmail и Yandex и создадим объекты этих классов.
На практике такой код не делал бы ровно делом ничего. Мы просто создали классы-пустышки и объекты-пустышки. Давайте чуть-чуть добавим реализма в наш пример по созданию класса Mail.
Я хочу заострить еще немного внимания на конструкторе, а именно на отличие переменных Login/login и Password/password. Я сделал это для того, чтобы компилятор, да и мы сами, понимали различие внутренних переменных класса login и password от передаваемых переменных в конструкторе Login и Password.
На самом деле - так не делают. Как минимум - называть переменные принято с маленькой буквы. Как же нам тогда быть? Как отличить внутренние переменные класса от тех, которые принимает конструктор? Нам на помощь приходит ключевое слово this.
Как объекты могут взаимодействовать с другим кодом программы или друг с другом?
Всё очень просто. Они взаимодействуют ровно так, как программист задумал это перед написание кода. Вы ведь не начнете писать шаблон на зенопостере, пока примерно не представите в голове скелет этого шаблона? Точно также в программировании - сначала "примерно" представляется что и как должен делать код и на какие сущности нужно поделить код, чтобы лаконично организовать их взаимодействией с поступающими данными, с кодом программы и друг с другом.
И как же нам лаконично всё это связать? Ведь программа может быть большой, да еще и неизвестно, как и кем её функционал будет меняться через неделю/месяц/год?
Умные люди на заре программирования подумали над этим вопросом и приняли на вооружение несколько принципов, и назвали их "фундаментальные принципы ООП", придерживание которым является не то, чтобы хорошим тоном, а необходимостью для хорошего разработчика.
На любом собеседовании на позицию junior разработчика вас спросят про эти принципы. Существует 3 принципа ООП: инкапсуляция, наследование, полиморфизм. Не так давно в эту группу стали выделять 4й принцип - абстракция. Вам пока не стоит задумываться о том, 3 принципа или 4, лучше мы рассмотрим примеры.
Инкапсуляция - это принцип ООП при котором разработчик скрывает внутреннюю реализацию класса и его методов в самом классе.
Перед тем, как перейти к кодовому примеру - давайте разберем пример из реальной жизни. Все знают, что такое автомобиль. Все знают, что у автомобиля есть мотор, коробка передач, подвеска и т.д. Когда вы садитесь в автомобиль - вы не не думаете о том, как устроен мотор, коробка передач или подвеска, но вам доступны элементы управления этими узлами: педали, ручка мкп/акп, кнопки для блокировки дифференциалов и т.п.
Это и есть пример инкапсуляции в реальной жизни. Первоначальный замысел - скрыть от пользователя внутреннюю реализацию узлов автомобиля. Если нужно, то конечно в эти узлы можно залезть при поломке, но в остальное время - просто нажимай педали и передвигай ручку акп/мпк и не думай о том, как крутятся шестерни.
Давайте теперь рассмотрим пример инкапсуляции в программе. Для этого нам пригодится наш ранее созданный класс Mail. Все примеры я связываю с реальной разработкой ботов для рассылок (т.к. это моя ниша) и упрощаю их для вашего понимая.
Напишем примерную абстрактную модель поведения аккаунта при отправке письма:
1) Авторизовались с помощью логина и пароля
2) Если аворизация прошла успешно, то нужно спарсить все необходимые куки для наших запросов
3) Если мы удачно спарсили куки, то можно отправить письмо. Для письма нам нужны текст и тема.
Теперь воссоздадим такую модель в нашем классе.
При воссоздании такой модели я для себя решил, что пользователю моего кода (это может быть другой пограммист) не обязательно разбираться в том, как авторизуется аккаунт или как он парсит куки.
Я хочу, чтобы другому разработчику нужно было банально указать текст и тему - остальное произойдет само и письмо будет отправлено. Другими словами - я хочу скрыть от другого прогаммиста всё, в чём ему не обязательно разбираться.
Мы указали заведомо правильные логин и пароль. Текст и тему мы указали не пустыми, поэтому проблем с отправкой сообщения возникнуть не может. Как результат - успешная отправка письма.
В качестве эксперемента давайте попробуем повторить отправку письма еще 2 раза. Для этого мы создадим два новых аккаунта (объекта). В одном специально укажем неправильный пароль, а в другом укажем пустую тему для письма.
Как результат - оба аккаунта не смогли отправить письмо:
Модификаторы доступа
Внимательные читатели обратили внимание, что у классов, полей и методов я пишу приставку public и private - что это такое? Это называется модификатором доступа. Модификаторы доступа идут бок о бок с инкапсуляцией.
Если объяснить очень просто, то:
public - используется у тех компонентов кода, к которым я могу обратится в любом месте своего кода
private - используется, когда я не хочу, чтобы к компонентам обращались из любого места в коде, т.к. это может нарушить логику работы
Вспоминаем мою изначальную задумку. Я хотел, чтобы другой программист, который захочет воспользоваться моим кодом, мог очень быстро отправить с помощью него письмо. Когда он начнет это делать, то он увидит, что у него в доступе есть только метод для отправки письма SendMsg. О других методах, которые авторизуют акаунт и получают куки он даже не будет знать:
Представим ситуацию, что у него на выбор был бы доступ к методу для получению куков, к методу для авториазации и к методу для отправки сообщения. Он, как не знаток рассылок, мог, на пример, попытаться получить куки до авторизации, и получил бы ошибку.
Это в нашем упрощенном примере всего 3 метода, которые выполняют простейшие действия. А теперь представьте ситуацию, когда таких методов будет десяток, да еще и со сложной логикой внутри. Зачем программисту в этом капаться? - Правильно, не за чем.
На скрине показан хороший пример того, как работают модификаторы доступа. Нам доступны только те методы, которые имеют модификатор public.
Вывод по Инкапсуляции
В совокупности - это и называется инкапсуляцией. Я, с помощью правильного написания кода и модификаторов доступа, скрыл от других внутреннюю реализацию класса в самом классе. Я открыл доступ только к тем функциям, которые посчитал нужными. Теперь другие программисты (и я сам, через пару лет) сможем воспользоваться данным кодом и быстро понять, что к чему.
Как вы думаете, сильно ли меняется автомобиль от модели к модели? Инженеры каждый раз создают новый автомобиль, или же пользуются старыми наработками? - Ответ очевиден, они пользуются предыдущим опытом.
Визуально автомобиль выглядит по другому от модели к модели, но концептуально - этот один и тот же "гольф". Другими словами все новые версии наследуют концепцию, формфактор, размер, средний объем двигателя и т.п. от твоего родителя - первой версии "гольфа". Но, при этом, новые модели "гольфа" обзаводятся другими функциями, другой внешностью и т.п.
В итоге - автомобиль наследует основные параметры и функции, но имеет и свои особенности от модели к модели.
Теперь посмотрим как происходит наследование в c# и других си-подобных языках.
Наследование (inheritance) является одним из ключевых принципов ООП. Благодаря наследованию один класс может унаследовать функциональность другого класса.
Для примера вспоминаем 3 популярных почтовика: mail, gmail и yandex. У всех из них есть схожие функции, такие как отправка письма или чтение входящих сообщений.
Представляем себе абстрактную задачу - нужно сделать бота, который умеет работать и с майлом, и с гуглом, и с яндексом. В нашей абстракции мы пренебригаем тем фактом, что реализация у каждого почтовика будет разная. Нам достаточно того, что у каждого почтовика есть схожий функционал - отправка собщения.
Я, как истинно ленивый прораммист (шутка), не хочу переписывать один и тот же функционал для каждого почтовика. Я попытаюсь обойти эту проблему.
Для начала я возьму весь код для реализации класса Mail, который был в инкапсуляции. Единственное, что я сделаю - это сменю название у класса (и конструктора) на EmailService - это будет класс-родитель. Как мы помним, в нём полностью реализован функционал отправки письма.
Зачем мне переписывать данный код для каждого почтовика, если я могу его просто позаимствовать?
Зачем мы проделали все эти действия? А вот зачем. Теперь мы можем делать отправку письма с любого аккаунта, будь-то Mail, Gmail или yandex. Мы добились это с помощью Наследования. Мы реализовали всю логику отпраки письма в классе-родителе
Как итог - успешная отправка со всех трёх почтовиков:
Давайте чуть-чуть добавим реализма в наш код. Мы прекрасно понимаем, что 3 данных почтовика не являются идентичными на 100%. Например у аккаунта gmail есть доступ к ютубу, у аккаунта yandex есть доступ к дзену, а у аккаунта mail есть доступ к Моему Миру.
Реализуем это у классов-наследников в виде кода:
В качестве примера воспользуемся собсвенным методом для создания Моего Мира у класса Mail:
Вывод по Наследованию
Принцип наследование позволяет нам выделить общие признаки и реализовать их в отдельной сущности (класс-родитель). Простыми словами - мы можем вынести за скобку общий функционал, который будет един для всех классов-наследников. Еще проще - мы избавляемся от дублирования кода, переиспользуя код класса-родителя.
Помимо этого, мы можем дополнять классы-наследники своим уникальным функционалом. Помимо этого мы можем отслеживать четкую иерархию классов, что не мало важно при создании крупных проектов.
Приставка poly - означает, что в составе объекта, к которому добавляется эта приставка, есть какая-то разновидность. Например: полиглот - человек, который знает несколько языков; Полимер - вещество, состоящие из нескольких других веществ (мономеров) и т.п.
Слово morfling - означает трансформацию. Еще можно употребить слово аморфный - т.е. не имеющий какого-то единного жесткого состояния.
В итоге получаем, что полиморфизм - это принцип, при котором какой-то объект может трансформироваться и реализовываться во множественных вариациях.
Обратимся к нашим любимым автомобилям для рассмотрения реального примера из жизни. Мы можем сделать так, чтобы автомобиль ехал. Другими словами - "ехать" это стандартная функция для автомобиля. Давайте теперь вспомним, что автомобиль может ехать вперед, назад, направо, налево, в горку, с горки, в сугробах, по кочкам и т.п.
Будете ли вы делать совершенно одни и теже же действия в каждом из вариантов езды? - Нет, ваши действия будут отличатся, но при этом вы будете делать одно и тоже действие - ехать. Это и есть пример полиморфизма - одно и тоже действие будет реализовано по разному.
Если рассматривать реализацию полиморфизма в программировании, то в первом приближении это означает, что разные классы могут реализовывать один и тот же метод по разному.
В наших упрощенных примерах мы создавали метод для отравки письма SendMsg. Мы прекрасно понимаем, что в реальном боте отправка писем с разных почтовиков будет выглядить по разному. Будут совершенно разные куки, запросы будут содержать различный набор данных, ссылки для запросов будут совсем другие и т.п.
В рамках данной статьи я не буду показывать реальную реализацию отправки в трёх почтовиках, т.к. это потянет еще на 20 статей, но я могу вам показать, как один и тот же метод может быть реализован в разных классах по своему. Для этого мы доработаем код, который остался после Наследования.
Мы создали 3 аккаунта для разных почтовиков и у каждого вызвали один и тот же метод SendMsg(). Как итог - для каждого аккаунта (т.е. для каждого класса-наследника) отработала своя рализация метода SendMsg().
Не забывем, что у нас есть разная реализация не только на уровне класса-наследников, но и на уровне самого метода (когда на вход поступают разные данные)
Как итог - все попытки отправить письмо с разным набором входных данных увенчались успехом:
Вывод по Полиморфизму
Благодаря возможностям языка c# мы можем делать разную реализацию одних и тех же методов. Откровенно говоря - одинковым является только имя метода, т.к. реализация метода будет разной для каждого класса. Нужно подметить, что полиморфизм существует не только для классов-наследников. Он существует независимо от иерархии классов. Вы можете создать методы с одиаковым названием в независящих друг от друга классов.
Зачем всё это нужно? Полиморфизм, как и другие принципи ООП направлены на облегчение не только написаня кода, но и для облегчения его редактирования и чтения. В вашем проекте может быть сотни классов, в которых подразумевается методы с однимии теми же навзаниями. Причем название подходит именно одно единственное, т.к. другое название может уже слегка запутать пограммиста-читальщика кода.
Представьте ситуацию, если бы мы писали софт не для 3их почтовиков, а для 30 почтовиков. Самое удобное название для метода отправки письма - это SendMsg() и я не хочу менять это название 30 раз. Я не хочу дежать в голове 30 названий методов, которые призваны для одного и того же.
Вполне вероятно, что в вашей программе будет менее очевидный по функционалу метод, например PrintResult(), который должен выводить в ЛОГ какой-то результат. Представьте ситуацию, что у вас 10 классов и в каждом есть необходимость выводить результат каких-то действий в ЛОГ. Самым верным путем будет реализация метода PrintResult() по принципу полиморфизма - в каждом классе своя реализция. Не нужно будет держать в голове 10 названий методов по выводу информации в ЛОГ.
2. Для определения переменных (полей) во время создания объектов - в классе необходимо сделать конструктор. Название конструктора такоеже, как название класса.
3. Чтобы непутать переменые (поля) класса с одноименными входными переменными конструктора нужно использовать ключевое слово this.
4. Существует 3 основных принципа ООП - инкапсуляция, наследование и полиморфизм.
5. Инкапсуляция - это принцип ООП, при котором принятно скрывать внутреннюю реализацю класса, а также его методов и внутренних переменных. Делается это с помощью правильного написания кода и модификаторов доступа.
6. Наследование - это принцип ООП, при котором принято переиспользовать код, чтобы его не дублировать. Сущесвуют классы-родители и классы-наследники. Классы-наследники могут унаследовать реализацию кода от класса-родителя.
7. Полиморфизм - это принцип ООП, при котором принято реализовывать один и тот же интерфейс разными способами. Мы можем писать разную реализацю одного и того же метода, но с разным набором входных параметров - это называется перегрузкой (полиморфизм на уровне методов). Компилятор сам поймет, как именно метод вызвать, по его входному набору параметров.
Мы можем писать различную реализацию одного и того же метода (имеется ввиду название метода), но в разных классах. Выполнение метода в данном случае будет зависить от того, объектом какого класса был вызван данный метод.
От себя хочу добавить, что максимальное раскрытие ООП достигается совместо с применением паттернов проектирования. Я обязательно расскажу об этом, если данная цепочка статей получит хороший отклик.
Для лучшего изучения я прикрепил к статье 3 шаблона (по одному на каждый принцип ООП). Минимальная верся зено 7.2
Мои контакты:
• Мой телеграм: https://t.me/brabus_soft
• Мой блог о рассылках: https://t.me/brabus_soft_channel
Спасибо за внимание!
В этой статье я хотел бы продолжить цепочку статей по изучению языка программирования c# в рамках Zennoposter.
Если вы не читали мою предыдущую конкурсную статью об изучении c#, которая заняла 3е место, то советую сперва прочитать её:
Начинаем кодить на c#
В этой статье я расскажу вам о том, что такое ООП и зачем оно нужно. Данный материал, на мой взляд, вышел за рамки обычной статьи и превратился в полноценное руководство по азам ООП. Как следствие - материала получилось много.
У данной статьи есть минималный порог входа:
1) Нужно знать азы c#, которые я описал в первой статье по c#
2) Нужно уметь писать базовые шаблоны на Zennoposter
Если вы не усвоили минималный порог, то понять данную статью вам будет не просто)
ООП - объектно-ориентированное программирование. Из самого определения нам становится понятно, что данная методология программирования направлена на представление кода в виде объектов (сущностей), которые по заданной нами логике взаимодействуют с кодом и друг с другом.
Если вы когда-нибудь задумаетесь о карьере программиста и попадёте на собеседование на junior позицию, то первое, что вас спросят - это ООП и его принципы. Естественно, в этом примере я говорю о программировании на си-подобных языках с поддержкой ООП.
Я не зря в предыдущей статье о c# проговорил, что она посвящена ПРОЦЕДУРНОМУ подходу к программированию. Чтобы понять смысл ООП, нам сперва нужно вспомнить, что же такое процедурное программирование.
При процедурном программировании программа ожидает на входе какие-то данные, делает с ними то, что мы накодили и выдаёт результат.
Пример простого процедурнго кода:
//Создали переменную number со значением равным нулю
int number = 0;
//Выводим значение переменной в ЛОГ
project.SendInfoToLog("Значение переменной number: " + number.ToString());
//Прибавили к переменной единицу
number = number + 1;
//Выводим значение переменной в ЛОГ
project.SendInfoToLog("Значение переменной number: " + number.ToString());
//Прибавили к переменной единицу еще раз
number = number + 1;
//Выводим значение переменной в ЛОГ
project.SendInfoToLog("Значение переменной number: " + number.ToString());
//Прибавили к переменной единицу еще раз
number++;
//Выводим значение переменной в ЛОГ
project.SendInfoToLog("Значение переменной number: " + number.ToString());
Другими словами - код программы выполняется снизу вверх. От первой строчки кода банально вниз, до последней строчки кода.
Такой принцип программирования "актуален" до тех пор, пока код вашей программы умещается в условные 400-500 строк кода. Если ваша программа подразумевает постоянное масштабирование, обновление функционала и т.п, то спустя некоторое время вы обнаружите, что поддерживать такую программу становится всё сложнее и сложнее.
На помощь программистам пришло ООП. Оно позволило раздувать функционал прогаммы до любого обозримого размера и делать этот функционал вполне себе обслуживаемым и масштабируемым.
В рамках данной статьи мы начнем погружение в ООП с азов. Как мы помним, фундамент ООП - это какие-то объекты. Что за объекты, собственно говоря?
Объект в ООП - это сущность, которая имеет какие-то свойства, имеет какое-то состояние и реализует какие-то методы (функции). Чтобы ответить на этот вопрос точнее - нужно немного углубиться.
Классы и объекты. Что это такое?
Класс в ООП - это некий шаблон или модель, по которому создаются объекты.
Я профи в рассылках, поэтому давайте разберем это на примере моих любимых вебмейлинг-рассылок. Все знают, что существует множество почтовиков, но мы возьмем 3 самых крупных в СНГ - mail, gmail и yandex. Любой из нас в любое время может создать почту в любом из этих почтовиков.
Будут ли отличатся наши аккаунты по функционалу или внешнему виду в рамках одного почтовика? - Нет, не будут. Любой аккаунт, ваш или соседа, будет иметь одни и те же поля для заполнения (ФИО, возраст, ГЕО, логин, пароль и т.п.), а также будет иметь один и тот же набор функционала (отправить письмо, получить письмо, удалить письмо и т.п.).
Будут ли отличатся между собой аккаунты разных почтовиков? - Конечно будут, хотя у них и есть похожий функционал (отправка письма, чтения письма и т.п.). У того же аккаунта gmail будет доступ к ютубу, у аккаунта yandex будет доступ к dzen, а у аккаунта mail будет доступ к Моему Миру.
Делаем вывод, что в этом примере mail, gmail и yandex - это классы, или другими словами - шаблоны, по которому создаются объекты, т.е. аккаунты. В каждом классе зашита своя логика, на основе которой создаются объекты.
Объекты взаимодействуют с кодом нашей программы. Также объекты разных классов могут взаимодействовать друг с другом. Аккаунт mail может отправить письмо на аккаунт gmail. Аккаунт yandex может удалить письмо от mail.
Давайте для примера создадим классы Mail, Gmail и Yandex и создадим объекты этих классов.
Пример создания классов (общий код):
public class Mail
{
//тут какая-то реализация класса Mail
}
public class Gmail
{
//тут какая-то реализация класса Gmail
}
public class Yandex
{
//тут какая-то реализация класса Yandex
}
Пример создание объектов (кубик c#):
//создадим 3 объекта, по одному на каждый класс
Mail mail_account_1 = new Mail();
Gmail gmail_account_1 = new Gmail();
Yandex mail_account_1 = new Yandex();
//создадим еще 3 объекта, по одному на каждый класс
Yandex mail_account_2 = new Yandex();
Mail mail_account_2 = new Mail();
Gmail gmail_account_2 = new Gmail();
/*
Разберем формат записи на примере одной строки
Mail mail_account_1 = new Mail();
Mail - это тип объекта, который равняется названию класса
mail_account_1 - это название объекта, который мы создаём
new Mail() - означает, что мы хотим создать новый объект
Если вы внимательно читали предыдущую статью, то вы заметите,
что формат создания объекта похож на формат создания обычной переменной:
int nubmer_1 = 10;
*/
На практике такой код не делал бы ровно делом ничего. Мы просто создали классы-пустышки и объекты-пустышки. Давайте чуть-чуть добавим реализма в наш пример по созданию класса Mail.
Пример создания класса Mail с переменными и конструктором (общий код):
public class Mail
{
//У любого аккаунта есть логин и пароль
public string login { get; set; }
public string password { get; set; }
public Mail(string Login, string Password)
{
login = Login;
password = Password;
}
}
/*
get и set (от англ. взять и дать, получить и установить)
Дают нам возможность установить или получить значения переменных
в любом удобном месте кода.
public Mail(string Login, string Password)
Этот блок называется Конструктор. Он выполняется в коде при создании объекта
и позволяет установить переменным какие-либо значения. В нашем случае - для
переменных login и password. Обратите внимание, что название контруктора равняется
названию класса.
Конструктор принимет на вход значения Login и Password и передаёт данные значения
во внутренние переменные класса login и password. Названия отличаются первой заглавной
буквой не просто так, иначе была бы путанница и компилятор выдал бы ошибку.
*/
Пример создания объекта класса Mail с использованием конструктора. Пример использования get и set (кубик c#):
Mail mailAccount1 = new Mail("[email protected]", "dima123");
/*
Благодаря конструктору мы установили значение логина и пароля
прямо во время создания объекта.
*/
project.SendInfoToLog("Логин: " + mailAccount1.login);
project.SendInfoToLog("Пароль: " + mailAccount1.password);
/*
Благодаря методу get мы можем получить значения переменных класса,
и, как пример, вывести их в ЛОГ
*/
mailAccount1.login = "[email protected]";
mailAccount1.password = "vasya123";
/*
Благодаря методу set мы можем установить новые значения для
переменных класса
*/
project.SendInfoToLog("Новый логин: " + mailAccount1.login);
project.SendInfoToLog("Новый пароль: " + mailAccount1.password);
/*
Благодаря методу get снова заглянем в значения внутренних переменных класса
login и password
*/
Пример использование ключевго слова this в классе.:
public class Mail
{
//У любого аккаунта есть логин и пароль
public string login { get; set; }
public string password { get; set; }
public Mail(string login, string password)
{
this.login = login;
this.password = password;
}
}
/*
Ключевое слово this помогает нам разрешить конфликт в названиях переменных,
когда входящий параметр (в нашем случае - входящий параметр конструктора)
назван так же, как внутренее поле(переменная) класса.
Зачем это нужно?
Мы ведь можем назвать входные параметры конструктора как угодно?
Хоть "asdasd", хоть "login1", хоть "loginDlyaConstructora". Попробуйте оценить такие
названия хотя бы визуально - выглядит как будто код писал начинающий программист в 5м классе.
Переменные должны быть названы так, чтобы человек без опыта взглянул на них и сразу понял,
зачем они нужны, не говоря уже о опытном программисте, который за 10 лет привык придерживаться
четких правил в обозначении переменных.
*/
Как объекты могут взаимодействовать с другим кодом программы или друг с другом?
Всё очень просто. Они взаимодействуют ровно так, как программист задумал это перед написание кода. Вы ведь не начнете писать шаблон на зенопостере, пока примерно не представите в голове скелет этого шаблона? Точно также в программировании - сначала "примерно" представляется что и как должен делать код и на какие сущности нужно поделить код, чтобы лаконично организовать их взаимодействией с поступающими данными, с кодом программы и друг с другом.
И как же нам лаконично всё это связать? Ведь программа может быть большой, да еще и неизвестно, как и кем её функционал будет меняться через неделю/месяц/год?
Умные люди на заре программирования подумали над этим вопросом и приняли на вооружение несколько принципов, и назвали их "фундаментальные принципы ООП", придерживание которым является не то, чтобы хорошим тоном, а необходимостью для хорошего разработчика.
На любом собеседовании на позицию junior разработчика вас спросят про эти принципы. Существует 3 принципа ООП: инкапсуляция, наследование, полиморфизм. Не так давно в эту группу стали выделять 4й принцип - абстракция. Вам пока не стоит задумываться о том, 3 принципа или 4, лучше мы рассмотрим примеры.
Инкапсуляция. Что это такое? Примеры
Начнем с понятия этого непонятного слова "Инкапсуляция". Переводим слово на англ. транслит и получаем фразу "in capsule", что переводится как "в капсуле". В итоге имеет, что мы что-то положили в капсулу, отгородив это от чего-то другого - это и есть основа инкапсуляции.
Инкапсуляция - это принцип ООП при котором разработчик скрывает внутреннюю реализацию класса и его методов в самом классе.
Перед тем, как перейти к кодовому примеру - давайте разберем пример из реальной жизни. Все знают, что такое автомобиль. Все знают, что у автомобиля есть мотор, коробка передач, подвеска и т.д. Когда вы садитесь в автомобиль - вы не не думаете о том, как устроен мотор, коробка передач или подвеска, но вам доступны элементы управления этими узлами: педали, ручка мкп/акп, кнопки для блокировки дифференциалов и т.п.
Это и есть пример инкапсуляции в реальной жизни. Первоначальный замысел - скрыть от пользователя внутреннюю реализацию узлов автомобиля. Если нужно, то конечно в эти узлы можно залезть при поломке, но в остальное время - просто нажимай педали и передвигай ручку акп/мпк и не думай о том, как крутятся шестерни.
Давайте теперь рассмотрим пример инкапсуляции в программе. Для этого нам пригодится наш ранее созданный класс Mail. Все примеры я связываю с реальной разработкой ботов для рассылок (т.к. это моя ниша) и упрощаю их для вашего понимая.
Напишем примерную абстрактную модель поведения аккаунта при отправке письма:
1) Авторизовались с помощью логина и пароля
2) Если аворизация прошла успешно, то нужно спарсить все необходимые куки для наших запросов
3) Если мы удачно спарсили куки, то можно отправить письмо. Для письма нам нужны текст и тема.
Теперь воссоздадим такую модель в нашем классе.
При воссоздании такой модели я для себя решил, что пользователю моего кода (это может быть другой пограммист) не обязательно разбираться в том, как авторизуется аккаунт или как он парсит куки.
Я хочу, чтобы другому разработчику нужно было банально указать текст и тему - остальное произойдет само и письмо будет отправлено. Другими словами - я хочу скрыть от другого прогаммиста всё, в чём ему не обязательно разбираться.
Пример реализации класса Mail (общий код):
//Создаём класс Mail
public class Mail
{
//Cоздадим поля(переменные) класса и конструктор
public string login { get; set; }
public string password { get; set; }
public Mail(string login, string password)
{
this.login = login;
this.password = password;
}
/*
Создадим 2 переменные класса: authResult и getCookiesResult
authResult - будет хранить в себе результат авторизации.
Если true - аккаунт авторизовался успешно;
Если false - аккаунт не смог авторизоваться
(например из-за ввода неправильного логина или пароля).
getCookiesResult - будет хранить в себе результат парсинга куков.
Изначально эти переменные равняются false и принимают значение true
только по ходу выпонения кода (после авторизации и получения куков соответственно)
*/
private bool authResult = false;
private bool getCookiesResult = false;
/*
Создадим метод Auth, который будет отвечать за авторизацию аккаунта. На вход он
примимает 2 параметра - логин и пароль.
Если логин и пароль совпадают с теми значениями, которыя я, для примера,
задал изначально "[email protected]" и "petr123", то авторизация считается
успешной и переменной authResult присваивается значение true.
Данная функция ничего и никуда не передаёт, поэтому она имеет тип void. Она
просто переопределяет переменную authResult в засисимости от успеха авторизации.
*/
private void Auth(string login, string password)
{
if(login == "[email protected]" && password == "petr123")
authResult = true;
else
authResult = false;
}
/*
Создадим метод Getcookies, который будет отвечать за получение куков.
В нашем упращенном примере мы принимает тот факт, что для получения
куков достаточно успешной авторизации на аккаунте. На реальных примерах
это действительно является одним из основных условий получения куков. Вы
не сможете получить нужных куков, если не авторизовались на аккаунте.
Данный метод проверяет переменную authResult на значение true или false.
Если значение true - то получение куков считается успешным и переменная
getCookiesResult принимает значение true, иначе - значение false.
Данная функция ничего и никуда не передаёт, поэтому она имеет тип void. Она
просто переопределяет переменную getCookiesResult в засисимости от успеха
получения куков.
*/
private void Getcookies()
{
if(authResult == true)
getCookiesResult = true;
else
getCookiesResult = false;
}
/*
Создадим метод SendMsg, который будет отвечать за отправку письма. На вход он
примимает 2 параметра - текст и тему.
Для отправки письма нам нужно авторизоваться и получить куки. Для это, в данном
методе мы обращаемся к ранее созданым методам Auth и Getcookies.
Для финального действия, т.е. для отправки письма, мы проверяем, что куки были
успешно получены, т.е. переменная getCookiesResult должна быть true. Также мы проверяем,
что текст и тема не являются пустыми (зачем нам отправлять пустое сообщение?).
Если все условия соблюдены, то сообщение считается отправленым и мы получаем результат
"Успешно отправили письмо", иначе - получем результат "Не смогли отправить письмо".
*/
public string SendMsg(string text, string title)
{
Auth(login, password);
Getcookies();
if(getCookiesResult == true && text != "" && title != "")
return "Успешно отправили письмо";
else
return "Не смогли отправить письмо";
}
}
Отправка письма с помощью класса Mail (c# кубик):
/*
Создаём объект с названием mailAccount класса Mail.
В качестве логина и пароля передаём заведомо правильные логин и пароль.
*/
Mail mailAccount = new Mail("[email protected]", "petr123");
/*
Отправляем сообщение с аккаунта(объекта) mailAccount с помощю метода SendMsg.
В качестве текста и темы передаём набор букв "asd" и "zxc".
*/
return mailAccount.SendMsg("asd", "zxc");
Мы указали заведомо правильные логин и пароль. Текст и тему мы указали не пустыми, поэтому проблем с отправкой сообщения возникнуть не может. Как результат - успешная отправка письма.
В качестве эксперемента давайте попробуем повторить отправку письма еще 2 раза. Для этого мы создадим два новых аккаунта (объекта). В одном специально укажем неправильный пароль, а в другом укажем пустую тему для письма.
Пример отправки письма с ошибками с помощью класса Mail (c# кубик):
Mail mailAccount1 = new Mail("[email protected]", "petr123456"); //специально указали неверный пароль для аккаунта mailAccount1
Mail mailAccount2 = new Mail("[email protected]", "petr123");
string result1 = mailAccount1.SendMsg("asd", "zxc");
string result2 = mailAccount2.SendMsg("asd", ""); //специально сделали тему письма пустой для аккаунта mailAccount2
project.SendInfoToLog("Результат отправки аккаунта1: " + result1);
project.SendInfoToLog("Результат отправки аккаунта2: " + result2);
Как результат - оба аккаунта не смогли отправить письмо:
Модификаторы доступа
Внимательные читатели обратили внимание, что у классов, полей и методов я пишу приставку public и private - что это такое? Это называется модификатором доступа. Модификаторы доступа идут бок о бок с инкапсуляцией.
Если объяснить очень просто, то:
public - используется у тех компонентов кода, к которым я могу обратится в любом месте своего кода
private - используется, когда я не хочу, чтобы к компонентам обращались из любого места в коде, т.к. это может нарушить логику работы
Вспоминаем мою изначальную задумку. Я хотел, чтобы другой программист, который захочет воспользоваться моим кодом, мог очень быстро отправить с помощью него письмо. Когда он начнет это делать, то он увидит, что у него в доступе есть только метод для отправки письма SendMsg. О других методах, которые авторизуют акаунт и получают куки он даже не будет знать:
Это в нашем упрощенном примере всего 3 метода, которые выполняют простейшие действия. А теперь представьте ситуацию, когда таких методов будет десяток, да еще и со сложной логикой внутри. Зачем программисту в этом капаться? - Правильно, не за чем.
На скрине показан хороший пример того, как работают модификаторы доступа. Нам доступны только те методы, которые имеют модификатор public.
Вывод по Инкапсуляции
В совокупности - это и называется инкапсуляцией. Я, с помощью правильного написания кода и модификаторов доступа, скрыл от других внутреннюю реализацию класса в самом классе. Я открыл доступ только к тем функциям, которые посчитал нужными. Теперь другие программисты (и я сам, через пару лет) сможем воспользоваться данным кодом и быстро понять, что к чему.
Наследование. Что это такое? Примеры
Наследование - что же это такое? Понять данный принцип ООП достаточно просто, т.к. банально что-то у кого-то наследуется, прямо как в обычной жизни. Вспоминаем про автомобили. Для примера рассмотрим самый большой концерт - Volkswagen, а именно - его самый популярный автомобиль Volkswagen Golf.
Как вы думаете, сильно ли меняется автомобиль от модели к модели? Инженеры каждый раз создают новый автомобиль, или же пользуются старыми наработками? - Ответ очевиден, они пользуются предыдущим опытом.
Визуально автомобиль выглядит по другому от модели к модели, но концептуально - этот один и тот же "гольф". Другими словами все новые версии наследуют концепцию, формфактор, размер, средний объем двигателя и т.п. от твоего родителя - первой версии "гольфа". Но, при этом, новые модели "гольфа" обзаводятся другими функциями, другой внешностью и т.п.
В итоге - автомобиль наследует основные параметры и функции, но имеет и свои особенности от модели к модели.
Теперь посмотрим как происходит наследование в c# и других си-подобных языках.
Наследование (inheritance) является одним из ключевых принципов ООП. Благодаря наследованию один класс может унаследовать функциональность другого класса.
Для примера вспоминаем 3 популярных почтовика: mail, gmail и yandex. У всех из них есть схожие функции, такие как отправка письма или чтение входящих сообщений.
Представляем себе абстрактную задачу - нужно сделать бота, который умеет работать и с майлом, и с гуглом, и с яндексом. В нашей абстракции мы пренебригаем тем фактом, что реализация у каждого почтовика будет разная. Нам достаточно того, что у каждого почтовика есть схожий функционал - отправка собщения.
Я, как истинно ленивый прораммист (шутка), не хочу переписывать один и тот же функционал для каждого почтовика. Я попытаюсь обойти эту проблему.
Для начала я возьму весь код для реализации класса Mail, который был в инкапсуляции. Единственное, что я сделаю - это сменю название у класса (и конструктора) на EmailService - это будет класс-родитель. Как мы помним, в нём полностью реализован функционал отправки письма.
Зачем мне переписывать данный код для каждого почтовика, если я могу его просто позаимствовать?
Пример использования Наследования (общий код):
/*
Создаём класс EmailService, который на 100%
идентичен классу Mail из инкапсуляции. Изменили
только название класса и конструктора
*/
public class EmailService
{
/*-----------------------------------------------------*/
public string login { get; set; }
public string password { get; set; }
public EmailService(string login, string password)
{
this.login = login;
this.password = password;
}
/*-----------------------------------------------------*/
private bool authResult;
private bool getCookiesResult;
/*-----------------------------------------------------*/
private void Auth(string login, string password)
{
if(login == "[email protected]" && password == "petr123")
authResult = true;
else
authResult = false;
}
/*-----------------------------------------------------*/
private void Getcookies()
{
if(authResult == true)
getCookiesResult = true;
else
getCookiesResult = false;
}
/*-----------------------------------------------------*/
public string SendMsg(string text, string title)
{
Auth(login, password);
Getcookies();
if(getCookiesResult == true && text != "" && title != "")
{
return "Успешно отправили письмо";
}
else
return "Не смогли отправить письмо";
}
}
/*
Создаём класс Gmail и делаем его наследником класса EmailService через двоиточие.
Не забываем прописать и конструктор.
*/
public class Gmail: EmailService
{
public Gmail(string login, string password) : base(login, password)
{
}
}
/*
Создаём класс Yandex и делаем его наследником класса EmailService через двоиточие.
Не забываем прописать и конструктор.
*/
public class Yandex: EmailService
{
public Yandex(string login, string password) : base(login, password)
{
}
}
/*
Создаём класс Mail и делаем его наследником класса EmailService через двоиточие.
Не забываем прописать и конструктор.
*/
public class Mail: EmailService
{
public Mail(string login, string password) : base(login, password)
{
}
}
/*
Ключевое слово base у конструкторов классов-наследников нужно для того,
чтобы мы могли воспользоваться конструктором класса-родителя, т.к. в нём
уже есть вся необходимая логика.
Другими словами - мы переопределили конструкторы классов-наследников и сослались
на конструктор класса-родителя.
Мы могли бы сделать и обычную реализацию конструктора у классов-наследников, например:
public class Yandex: EmailService
{
public Yandex(string login, string password)
{
this.login = login;
this.password = password;
}
}
Но мы вообще-то пытаемся избавиться от дублирования кода!)
Осбенно, когда такого кода не 2 строчки, а 200, как в реальных примерах.
*/
Зачем мы проделали все эти действия? А вот зачем. Теперь мы можем делать отправку письма с любого аккаунта, будь-то Mail, Gmail или yandex. Мы добились это с помощью Наследования. Мы реализовали всю логику отпраки письма в классе-родителе
Отправка письма с аккаунтов Mail, Gmail и Yandex благодаря Наследованию (c# кубик):
//Создали аккаунт mail на базе родительсого класса EmailService
EmailService mail = new Mail("[email protected]", "petr123");
//Создали аккаунт gmail на базе родительсого класса EmailService
EmailService gmail = new Gmail("[email protected]", "petr123");
//Создали аккаунт yandex на базе родительсого класса EmailService
EmailService yandex = new Yandex("[email protected]", "petr123");
/*
Т.к. мы унаследовали классы Mail, Gmail и Yandex от EmailService - нам
становится доступен весь функционал класса EmailService, включая авторизацию (Auth),
получение куков (getCookiesResult) и самое главное - отправка письма (SendMsg).
Класс EmailService является родителельским классом.
Классы Mail, Gmail и Yandex являются дочерними класами-наследниками.
*/
//Сделали отправку с аккаунта mail и сохранили результат отправки в переменную mailSendResult
string mailSendResult = mail.SendMsg("asd", "zxc");
//Сделали отправку с аккаунта gmail и сохранили результат отправки в переменную gmailSendResult
string gmailSendResult = gmail.SendMsg("asd", "zxc");
//Сделали отправку с аккаунта yandex и сохранили результат отправки в переменную yandexSendResult
string yandexSendResult = yandex.SendMsg("asd", "zxc");
//Выводим результат отправок в ЛОГ
project.SendInfoToLog("Результат отправки с mail: " + mailSendResult);
project.SendInfoToLog("Результат отправки с gmail: " + gmailSendResult);
project.SendInfoToLog("Результат отправки с yandex: " + yandexSendResult);
/*
В этом примере я прошу опустить тот факт, что у акаунтов mail, gmail и yandex
один и тот же логин и пароль, да еще и у логина домен - @mail.ru.
Понятное дело, что это можно обыграть, но тогда бы это заструднило процесс объяснения.
*/
Давайте чуть-чуть добавим реализма в наш код. Мы прекрасно понимаем, что 3 данных почтовика не являются идентичными на 100%. Например у аккаунта gmail есть доступ к ютубу, у аккаунта yandex есть доступ к дзену, а у аккаунта mail есть доступ к Моему Миру.
Реализуем это у классов-наследников в виде кода:
Пример собственных методов у классов-наследников (часть общего кода):
public class Gmail: EmailService
{
public Gmail(string login, string password) : base(login, password)
{
}
//Добавили метод для создания ютуб-аккаунта у Gmail
public string CreateYoutubeAccount()
{
return "Успешно создали youtube-аккаунт";
}
}
public class Yandex: EmailService
{
public Yandex(string login, string password) : base(login, password)
{
}
//Добавили метод для создания дзен-аккаунта у Yandex
public string CreateDzenAccount()
{
return "Успешно создали дзен-аккаунт";
}
}
public class Mail: EmailService
{
public Mail(string login, string password) : base(login, password)
{
}
//Добавили метод для создания моего мира у Mail
public string CreateMyWold()
{
return "Успешно создали мой мир";
}
}
Вывод по Наследованию
Принцип наследование позволяет нам выделить общие признаки и реализовать их в отдельной сущности (класс-родитель). Простыми словами - мы можем вынести за скобку общий функционал, который будет един для всех классов-наследников. Еще проще - мы избавляемся от дублирования кода, переиспользуя код класса-родителя.
Помимо этого, мы можем дополнять классы-наследники своим уникальным функционалом. Помимо этого мы можем отслеживать четкую иерархию классов, что не мало важно при создании крупных проектов.
Полиморфизм. Что это такое? Примеры
Полиморфизм - что же это такое? Давайте для начала, как обычно, поймем состав данного слова. Слово состоит из двух друг слов: poly и morfling(вместо этого слова можно подобрать другие)
Приставка poly - означает, что в составе объекта, к которому добавляется эта приставка, есть какая-то разновидность. Например: полиглот - человек, который знает несколько языков; Полимер - вещество, состоящие из нескольких других веществ (мономеров) и т.п.
Слово morfling - означает трансформацию. Еще можно употребить слово аморфный - т.е. не имеющий какого-то единного жесткого состояния.
В итоге получаем, что полиморфизм - это принцип, при котором какой-то объект может трансформироваться и реализовываться во множественных вариациях.
Обратимся к нашим любимым автомобилям для рассмотрения реального примера из жизни. Мы можем сделать так, чтобы автомобиль ехал. Другими словами - "ехать" это стандартная функция для автомобиля. Давайте теперь вспомним, что автомобиль может ехать вперед, назад, направо, налево, в горку, с горки, в сугробах, по кочкам и т.п.
Будете ли вы делать совершенно одни и теже же действия в каждом из вариантов езды? - Нет, ваши действия будут отличатся, но при этом вы будете делать одно и тоже действие - ехать. Это и есть пример полиморфизма - одно и тоже действие будет реализовано по разному.
Если рассматривать реализацию полиморфизма в программировании, то в первом приближении это означает, что разные классы могут реализовывать один и тот же метод по разному.
В наших упрощенных примерах мы создавали метод для отравки письма SendMsg. Мы прекрасно понимаем, что в реальном боте отправка писем с разных почтовиков будет выглядить по разному. Будут совершенно разные куки, запросы будут содержать различный набор данных, ссылки для запросов будут совсем другие и т.п.
В рамках данной статьи я не буду показывать реальную реализацию отправки в трёх почтовиках, т.к. это потянет еще на 20 статей, но я могу вам показать, как один и тот же метод может быть реализован в разных классах по своему. Для этого мы доработаем код, который остался после Наследования.
Пример полиморфизма (общий код):
public abstract class EmailService
{
/*-----------------------------------------------------*/
public string login { get; set; }
public string password { get; set; }
public EmailService(string login, string password)
{
this.login = login;
this.password = password;
}
/*-----------------------------------------------------*/
private bool authResult;
private bool getCookiesResult;
/*-----------------------------------------------------*/
private void Auth(string login, string password)
{
if(login == "[email protected]" && password == "petr123")
authResult = true;
else
authResult = false;
}
/*-----------------------------------------------------*/
private void Getcookies()
{
if(authResult == true)
getCookiesResult = true;
else
getCookiesResult = false;
}
/*
Создаём обстрактный метод SendMsg() с помощью ключевого слова abstract.
У данного метода нет никакой реализации, поэтому после объявления метода
просто ставим точку с запятой ";"
Он называется абстрактным, т.к. он не является чем-то конктретным. У него даже нет
входящих параметров. Он просто существует, как какое-то аморфное существо.
Мы это сделали, чтобы у нас была возможность сделать несколько вариантов реализации
данного метода (poly, помните?)
*/
public abstract string SendMsg();
/*
Создали дополнительный метод CreoValidation, который проверяет текст и тему на пустоту.
Это никак не относится к полиморфизму. Просто я захотел для такой проверки
внаписать специальный метод.
Если текст или тема пустая - результатом будет false.
Если текст и тема не пустые - результатом будет true.
*/
private bool CreoValidation(string text, string title)
{
if(text != "" && title != "")
return true;
else
return false;
}
/*
1ая реализация метода SendMsg, при котором на вход поступают
только текст и тема, но нет инфрмации о том, кому нужно отправить письмо.
В реальном проекте такое может означать, что письмо нужно отправить самому себе.
Каждая такая реализация называется "Перегрузкой" метода. Можно сказать, что это
полиморфизм на уровне методов - каждый раз будет разный набор входных данных.
*/
public string SendMsg(string text, string title)
{
if(CreoValidation(text, title))
{
return SendMsg(); //Это что еще такое? Один SendMsg() внутри другого SendMsg()? Ответ будет в классе-наследнике
}
else
return "Не удалось отправить письмо";
}
/*
2ая реализация метода SendMsg, при котором на вход поступают
текст, тема и почта адресата.
В реальном проекте такое может означать обычную отправку письма любому адресату.
*/
public string SendMsg(string text, string title, string emailAddress)
{
if(CreoValidation(text, title))
{
return SendMsg(); //Это что еще такое? Один SendMsg() внутри другого SendMsg()? Ответ будет в классе-наследнике
}
else
return "Не удалось отправить письмо";
}
/*
3ая реализация метода SendMsg, при котором на вход поступают
текст, тема, почта адресата, а также число отправок.
В реальном проекте такое может означать отправку письма любому адресату, но несколько раз.
*/
public string SendMsg(string text, string title, string emailAddress, int sendNumber)
{
if(CreoValidation(text, title))
{
return SendMsg(); //Это что еще такое? Один SendMsg() внутри другого SendMsg()? Ответ будет в классе-наследнике
}
else
return "Не удалось отправить письмо";
}
}
public class Gmail: EmailService
{
public Gmail(string login, string password) : base(login, password)
{
}
public string CreateYoutubeAccount()
{
return "Успешно создали youtube-аккаунт";
}
/*
А вот и ответ на загадку с SendMsg() внутри другого SendMsg().
Что мы сделали? Мы сделали для класса-наследника Gmail свою собствтенную
реализацию метода SendMsg(). В нашем упрощенном примере - это реализовано тем,
что при успешной оправке будет написано, что письмо отправленно именно с Gmail.
Мы можем сделать свою собственную реализацию метода SendMsg() для каждого
класса-наследника. Это и есть проявление полиморфизма.
Ключевое слово override означает, что мы написали конкретную реализацию
абстрактного метода. Речь идет именно о том, что мы реализовали абстрактный
метод класса-родителя в классе-наследнике.
*/
public override string SendMsg()
{
return "Успешно отправили письмо c Gmail";
}
}
public class Yandex: EmailService
{
public Yandex(string login, string password) : base(login, password)
{
}
public string CreateDzenAccount()
{
return "Успешно создали дзен-аккаунт";
}
/*
А вот и ответ на загадку с SendMsg() внутри другого SendMsg().
Что мы сделали? Мы сделали для класса-наследника Yandex свою собствтенную
реализацию метода SendMsg(). В нашем упрощенном примере - это реализовано тем,
что при успешной оправке будет написано, что письмо отправленно именно с Yandex.
*/
public override string SendMsg()
{
return "Успешно отправили письмо c Yandex";
}
}
public class Mail: EmailService
{
public Mail(string login, string password) : base(login, password)
{
}
public string CreateMyWold()
{
return "Успешно создали мой мир";
}
/*
А вот и ответ на загадку с SendMsg() внутри другого SendMsg().
Что мы сделали? Мы сделали для класса-наследника Mail свою собствтенную
реализацию метода SendMsg(). В нашем упрощенном примере - это реализовано тем,
что при успешной оправке будет написано, что письмо отправленно именно с Mail.
*/
public override string SendMsg()
{
return "Успешно отправили письмо c Mail";
}
}
Отправка письма с разных почтовиков, используя один и тот же метод SendMsg (c# кубик):
EmailService gmail = new Gmail("[email protected]", "petr123"); //Создали аккаунт (объект) Gmail на основе родительского класса EmailService
project.SendInfoToLog(gmail.SendMsg("asd", "zxc")); //Отправили сообщение с аккаунта gmail
EmailService mail = new Mail("[email protected]", "petr123"); //Создали аккаунт (объект) Mail на основе родительского класса EmailService
project.SendInfoToLog(mail.SendMsg("asd", "zxc")); //Отправили сообщение с аккаунта mail
EmailService yandex = new Yandex("[email protected]", "petr123"); //Создали аккаунт (объект) Yandex на основе родительского класса EmailService
project.SendInfoToLog(yandex.SendMsg("asd", "zxc")); //Отправили сообщение с аккаунта yandex
Мы создали 3 аккаунта для разных почтовиков и у каждого вызвали один и тот же метод SendMsg(). Как итог - для каждого аккаунта (т.е. для каждого класса-наследника) отработала своя рализация метода SendMsg().
Использование одного и тогоже метода, но с разным набором входных данных (c# кубик):
EmailService gmail = new Gmail("[email protected]", "petr123"); //Создали аккаунт (объект) Gmail на основе родительского класса EmailService
project.SendInfoToLog(gmail.SendMsg("asd", "zxc")); //Отправили сообщение с аккаунта gmail, используя только текст и тему
project.SendInfoToLog(gmail.SendMsg("asd", "zxc", "[email protected]")); //Отправили сообщение с аккаунта gmail, используя текст, тему и адресата
project.SendInfoToLog(gmail.SendMsg("asd", "zxc", "[email protected]", 5)); //Отправили сообщение с аккаунта gmail, используя текст, тему, адресатаи и число отправок
Благодаря возможностям языка c# мы можем делать разную реализацию одних и тех же методов. Откровенно говоря - одинковым является только имя метода, т.к. реализация метода будет разной для каждого класса. Нужно подметить, что полиморфизм существует не только для классов-наследников. Он существует независимо от иерархии классов. Вы можете создать методы с одиаковым названием в независящих друг от друга классов.
Зачем всё это нужно? Полиморфизм, как и другие принципи ООП направлены на облегчение не только написаня кода, но и для облегчения его редактирования и чтения. В вашем проекте может быть сотни классов, в которых подразумевается методы с однимии теми же навзаниями. Причем название подходит именно одно единственное, т.к. другое название может уже слегка запутать пограммиста-читальщика кода.
Представьте ситуацию, если бы мы писали софт не для 3их почтовиков, а для 30 почтовиков. Самое удобное название для метода отправки письма - это SendMsg() и я не хочу менять это название 30 раз. Я не хочу дежать в голове 30 названий методов, которые призваны для одного и того же.
Вполне вероятно, что в вашей программе будет менее очевидный по функционалу метод, например PrintResult(), который должен выводить в ЛОГ какой-то результат. Представьте ситуацию, что у вас 10 классов и в каждом есть необходимость выводить результат каких-то действий в ЛОГ. Самым верным путем будет реализация метода PrintResult() по принципу полиморфизма - в каждом классе своя реализция. Не нужно будет держать в голове 10 названий методов по выводу информации в ЛОГ.
Общий итог
1. В основе ООП лежат классы и объекты. Класс - это шаблон (или модель) по которой создаются объекты (экземпляры) класса. Класс является типом данных. Если вы создали объект класса Mail, то и тип данных у объекта будет Mail (не int, не string и т.п.)
2. Для определения переменных (полей) во время создания объектов - в классе необходимо сделать конструктор. Название конструктора такоеже, как название класса.
3. Чтобы непутать переменые (поля) класса с одноименными входными переменными конструктора нужно использовать ключевое слово this.
4. Существует 3 основных принципа ООП - инкапсуляция, наследование и полиморфизм.
5. Инкапсуляция - это принцип ООП, при котором принятно скрывать внутреннюю реализацю класса, а также его методов и внутренних переменных. Делается это с помощью правильного написания кода и модификаторов доступа.
6. Наследование - это принцип ООП, при котором принято переиспользовать код, чтобы его не дублировать. Сущесвуют классы-родители и классы-наследники. Классы-наследники могут унаследовать реализацию кода от класса-родителя.
7. Полиморфизм - это принцип ООП, при котором принято реализовывать один и тот же интерфейс разными способами. Мы можем писать разную реализацю одного и того же метода, но с разным набором входных параметров - это называется перегрузкой (полиморфизм на уровне методов). Компилятор сам поймет, как именно метод вызвать, по его входному набору параметров.
Мы можем писать различную реализацию одного и того же метода (имеется ввиду название метода), но в разных классах. Выполнение метода в данном случае будет зависить от того, объектом какого класса был вызван данный метод.
От себя хочу добавить, что максимальное раскрытие ООП достигается совместо с применением паттернов проектирования. Я обязательно расскажу об этом, если данная цепочка статей получит хороший отклик.
Для лучшего изучения я прикрепил к статье 3 шаблона (по одному на каждый принцип ООП). Минимальная верся зено 7.2
Мои контакты:
• Мой телеграм: https://t.me/brabus_soft
• Мой блог о рассылках: https://t.me/brabus_soft_channel
Спасибо за внимание!
- Номер конкурса статей
- Девятнадцатый конкурс статей
Вложения
-
9,8 КБ Просмотры: 117
-
10,1 КБ Просмотры: 107
-
11,2 КБ Просмотры: 105
Для запуска проектов требуется программа ZennoPoster или ZennoDroid.
Это основное приложение, предназначенное для выполнения автоматизированных шаблонов действий (ботов).
Подробнее...
Для того чтобы запустить шаблон, откройте нужную программу. Нажмите кнопку «Добавить», и выберите файл проекта, который хотите запустить.
Подробнее о том, где и как выполняется проект.
Последнее редактирование модератором: