Интерфейс php

Абстракция

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

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

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

Роль инкапсуляции

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

Т.е. инкапсуляция представляет собой способности языка скрывать излишние детали реализации от пользователя объекта.
Например, предположим, что используется класс по имени DatabaseReader, который
имеет два главных метода: Open() и Close().

Фиктивный класс DatabaseReader инкапсулирует внутренние детали нахождения,
загрузки, манипуляций и закрытия файла данных. Программистам нравится инкапсуляция, поскольку этот принцип ООП упрощает кодирование. Нет необходимости беспокоиться о многочисленных строках кода, которые работают «за кулисами», чтобы
реализовать функционирование класса DatabaseReader. Все, что потребуется — это
создать экземпляр и отправлять ему соответствующие сообщения (например, «открыть файл по имени AutoLot.mdf, расположенный на диске С:»).

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

Основной единицей инкапсуляции в C# является класс, который определяет форму
объекта. Он описывает данные, а также код, который будет ими оперировать. В C# описание класса служит для построения объектов, которые являются экземплярами
класса. Следовательно, класс, по существу, представляет собой ряд схематических описаний способа построения объекта.

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

Решение проблемы ромба

Решение достаточно простое. Хотя наверно в реальном коде его лучше не использовать. Да и большинство ОО-языков не позволяют этого сделать.

Но, … что если смоделировать это? Я хочу использовать повторное использование кода!

Тогда вы должны Contain и Delegate.

Class PoweredDevice {
}

Class Scanner inherits from PoweredDevice {
  function start() {
  }
}

Class Printer inherits from PoweredDevice {
  function start() {
  }
}

Class Copier {
  Scanner scanner
  Printer printer

  function start() {
    printer.start()
  }
}

Обратите внимание, что класс Copier теперь содержит экземпляр Printer и Scanner. Он использует функцию start реализации класса Printer

Он также может использовать класс Scanner.

Эта проблема — еще одна трещина в столпе Наследования.

Преимущества и варианты

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

Описательные имена

У фабричного метода есть собственное имя. Во многих объектно-ориентированных языках конструкторы должны иметь то же имя, что и класс, в котором они находятся, что может привести к неоднозначности, если существует более одного способа создания объекта (см. ). Фабричные методы не имеют такого ограничения и могут иметь описательные имена; их иногда называют альтернативными конструкторами . Например, когда комплексные числа создаются из двух действительных чисел, действительные числа можно интерпретировать как декартовы или полярные координаты, но с использованием фабричных методов смысл ясен, как показано в следующем примере на C #.

    public class Complex
    {
        public double real;
        public double imaginary;

        public static Complex FromCartesian(double real, double imaginary)
        {
            return new Complex(real, imaginary);
        }

        public static Complex FromPolar(double modulus, double angle)
        {
            return new Complex(modulus * Math.Cos(angle), modulus * Math.Sin(angle));
        }

        private Complex(double real, double imaginary)
        {
            this.real = real;
            this.imaginary = imaginary;
        }
    }

Complex product = Complex.FromPolar(1, Math.PI);

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

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

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

Рассмотрим в качестве примера программу, которая читает файлы изображений . Программа поддерживает различные форматы изображений, представленные классом чтения для каждого формата.

Каждый раз, когда программа считывает изображение, ей необходимо создать считыватель соответствующего типа на основе некоторой информации в файле. Эта логика может быть инкапсулирована в фабричный метод. Этот подход также называют простой фабрикой.

Джава

public class ImageReaderFactory {
    public static ImageReader createImageReader(ImageInputStreamProcessor iisp) {
        if (iisp.isGIF()) {
            return new GifReader(iisp.getInputStream());
        }
        else if (iisp.isJPEG()) {
            return new JpegReader(iisp.getInputStream());
        }
        else {
            throw new IllegalArgumentException("Unknown image type.");
        }
    }
}

PHP

class Factory
{
    public static function build($type)
    {
        $class = "Format" . $type;
        if (!class_exists($class)) {
            throw new Exception("Missing format class.");
        }
        return new $class;
    }
}

interface FormatInterface {}

class FormatString implements FormatInterface {}
class FormatNumber implements FormatInterface {}

try {
    $string = Factory::build("String");
} catch (Exception $e) {
    echo $e->getMessage();
}

try {
    $number = Factory::build("Number");
} catch (Exception $e) {
    echo $e->getMessage();
}

Мотивация

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

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

Что такое интерфейс?

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

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

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

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

Пример

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

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

Пример

Примечание: Интерфейсы не должны объявлять конструкторы или деструкторы, поскольку это детали реализации на уровне класса.

Приложения

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

Фабрики определяют фактический конкретный тип создаваемого объекта , и именно здесь объект фактически создается. Поскольку фабрика возвращает объекту только абстрактный интерфейс, клиентский код не знает — и не обременен — ​​фактическим конкретным типом только что созданного объекта. Однако абстрактной фабрике известен тип конкретного объекта. В частности, это означает:

  • Клиентский код ничего не знает о конкретном типе , и ему не нужно включать какие-либо файлы заголовков или объявления классов, относящиеся к конкретному типу. Клиентский код имеет дело только с абстрактным типом. Объекты конкретного типа действительно создаются фабрикой, но клиентский код обращается к таким объектам только через их абстрактный интерфейс .
  • Добавление новых конкретных типов выполняется путем модификации клиентского кода для использования другой фабрики, модификация, которая обычно представляет собой одну строку в одном файле. Это значительно проще, чем изменение клиентского кода для создания экземпляра нового типа, что потребовало бы изменения каждого места в коде, где создается новый объект.

15.17 Reflection API

(отражение) — процесс, во время которого программа может отслеживать и модифицировать собственную структуру и поведение во время выполнения. PHP включает в себя полноценный Reflection API, который предоставляет возможность проводить интроспекцию классов, интерфейсов, функций, методов и модулей. Кроме того, Reflection API позволяет получать doc-блоки комментариев функций, классов и методов.

Пример

/**
 * Class Profile
 */
class Profile {
   /**
    * @return string
    */
   public function getUserName(): string
   {
      return 'Foo';
   }
}

Класс Profile является черным ящиком. И если у нас нет доступа к коду, то посмотреть как он устроен можно используя ReflectionClass:

// получаем объект
$reflectionClass = new ReflectionClass('Profile');

// получаем имя класса
var_dump($reflectionClass->getName());
=> output: string(7) "Profile"

// получаем комментарии
var_dump($reflectionClass->getDocComment());
=> output:
string(24) "/**
 * Class Profile
 */"

ReflectionClass похож на аналитика для класса Profile, и это основная идея Reflection API.

PHP дает ключи к закрытым параметрам, поэтому мы можем получить доступ к ним с помощью:

ReflectionClass: сообщает информацию о классе.
ReflectionFunction: сообщает информацию о функции.
ReflectionParameter: извлекает информацию о параметрах функции или метода.
ReflectionClassConstant: сообщает информацию о константе класса.

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

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

15.11. Трейты вместо множественного наследования

В PHP нельзя наследовать от нескольких классов сразу, только от одного.

Чтобы обойти это ограничение, вы можете использовать композицию.
Однако в PHP есть и другой способ. Он заключается в использовании трейтов.

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

Синтаксис трейта такой же как и у класса, за исключением того, что имя трейта нужно объявлять с помощью ключевого слова trait.

Экземпляр трейта нельзя создать — трейты предназначены только для подключения к другим классам.

Пример

trait Hello {
    public function sayHello() {
        echo 'Hello ';
    }
}

trait World {
    public function sayWorld() {
        echo 'World';
    }
}

class MyHelloWorld {
    use Hello, World;
    public function sayExclamationMark() {
        echo '!';
    }
}

$o = new MyHelloWorld();
$o->sayHello();
$o->sayWorld();
$o->sayExclamationMark();

Для чего это всё

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

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

  • один программист делает функцию, которая отвечает за регистрацию новых пользователей;
  • все остальные используют эту функцию, привыкли к её параметрам и знают, что туда нужно передавать;
  • внезапно этот программист понимает, что можно запрограммировать более эффективный вариант регистрации, который будет работать гораздо быстрее;
  • он полностью переписывает функцию, и в этот момент у всей команды ломается логика программы: старые вызовы не работают, потому что функция теперь принимает другие данные (или в другой последовательности).

Чтобы такого не было, поступают так:

Один программист отвечает за регистрацию новых пользователей.
Он делает объект «Пользователь» и прописывает ему интерфейс «Зарегистрировать».
Также он говорит, что интерфейс «Зарегистрировать» будет принимать на вход три переменные: имя, почту и пароль.
Теперь программист говорит остальным: «Ребята, если нужно зарегистрировать нового пользователя — используйте интерфейс Пользователь.Зарегистрировать(имя,почта,пароль)».
Даже если внутри объекта поменяется вся логика работы, то это никак не повлияет на интерфейс — все так же смогут им пользоваться.
А всё потому, что никому нет дела, как тот программист реализовал свою часть, если через интерфейс всё работает нормально

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

Текст

Михаил Полянин

Редактор

Максим Ильяхов

Художник

Даня Берковский

Корректор

Ирина Михеева

Вёрстка

Мария Дронова

Соцсети

Олег Вешкурцев

Создание класса

Что имеет автомобиль? В частности, это:

  • марка;
  • цвет;
  • мощность (в л/с);
  • максимальная скорость (км/ч);
  • объём бака (л);
  • расход топлива (л) на 100 км пути.

Напишем класс Car (автомобиль) на C# (аналогично на Java):

C#

public class Car
{
private string brand;
private string
color;
private int power;
private int maxSpeed;
private int volumeOfTank;
private double fuelConsumption;
}

1
2
3
4
5
6
7
8
9

publicclassCar

{

privatestringbrand;

privatestringcolor;

privateintpower;

privateintmaxSpeed;

privateintvolumeOfTank;

privatedoublefuelConsumption;

}

Как вы могли заменить класс объявляется так: , ключевое слово class и имя класса. Тело класса определяется фигурными скобками. Внутри класса объявлены его поля.

Следует понимать, что класс — это каркас, иначе говоря, описание реального объекта. На основе этого «описания» создаются экземпляры реального объекта. Логично предположить, что необходим механизм для присваивания значениям полей характеристик объекта. Для этого существуют конструкторы класса.

Конструктор класса

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

Напишем конструктор для инициализации полей в классе Car:

C#

public class Car
{
private string brand;
private string color;
private int power;
private int maxSpeed;
private int volumeOfTank;
private double fuelConsumption;

//конструктор класса
public Car(string newBrand, string newColor, int newPower, int newMaxSpeed,
int newVolumeOfTank, double newFuelConsumption)
{
brand = newBrand;
color = newColor;
power = newPower;
maxSpeed = newMaxSpeed;
volumeOfTank = newVolumeOfTank;
fuelConsumption = newFuelConsumption;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

publicclassCar

{

privatestringbrand;

privatestringcolor;

privateintpower;

privateintmaxSpeed;

privateintvolumeOfTank;

privatedoublefuelConsumption;

//конструктор класса

publicCar(stringnewBrand,stringnewColor,intnewPower,intnewMaxSpeed,

intnewVolumeOfTank,doublenewFuelConsumption)

{

brand=newBrand;

color=newColor;

power=newPower;

maxSpeed=newMaxSpeed;

volumeOfTank=newVolumeOfTank;

fuelConsumption=newFuelConsumption;

}

}

Конструктор объявляется так: public Имя (). Наличие параметров не обязательно. Соответственно выделяют конструкторы класса:

  • без параметров
  • с параметрами

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

Конструктор по умолчанию — это пустой конструктор без параметров. Он всегда присутствует в классе (если нет других конструкторов), даже если он не был объявлен явно. Конструктор по умолчанию вызывается автоматически всегда, когда отсутствуют другие конструкторы. Его код это (писать не обязательно):

C#

public Car()
{

}

1
2
3
4

publicCar()

{
 
}

Класс может содержать несколько конструкторов с разными параметрами. При создании объекта будет вызван тот, который подходит по параметрам.

P.S. Ничего не запрещает написать в классе одновременно конструктор без параметров (явно; тогда им можно будет воспользоваться при создании нового экземпляра класса) и конструктор с параметрами.

Роль наследования

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

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

Если не пользоваться иерархиями, то для каждого объекта пришлось бы явно определять все его свойства. А если воспользоваться наследованием, то достаточно определить лишь те свойства, которые делают объект особенным в его классе. Он может также наследовать общие свойства своего родителя. Следовательно, благодаря механизму
наследования один объект становится отдельным экземпляром более общего класса.

15.5. Динамический вызов свойств и методов

Пример

<?php
class User
{
    public $name;
    public $age;
    
    public function __construct($name, $age)
    {
        $this->name = $name;
        $this->age = $age;
    }
}
<?php
$user = new User('Дарья', 30);

$prop = 'name';
echo $user->$prop; // выведет 'Дарья'

Пример

<?php
class User
{
    public $surname; // фамилия
    public $name; // имя
    public $patronymic; // отчество
    
    public function __construct($surname, $name, $patronymic)
    {
        $this->surname = $surname;
        $this->name = $name;
        $this->patronymic = $patronymic;
    }
}
<?php
	$user = new User('Иванов', 'Иван', 'Иванович');
	
	$props = ;
	echo $user->{$props[]}; // выведет 'Иванов'

Пример

<?php
class User
{
    private $name;
    private $age;
    
    public function __construct($name, $age)
    {
        $this->name = $name;
        $this->age = $age;
    }
    
    public function getName()
    {
        return $this->name;
    }
    
    public function getAge()
    {
        return $this->age;
    }
}
<?php
$user = new User('Дарья', 30);

$method = 'getName';
echo $user->$method(); // выведет 'Дарья'

Об именовании полей и методов

В среде объектно-ориентированно программирования при задании имени полей и методов принято использовать, так называемый Верблюжий Регистр (CamelCase).

Слова в именах членов класса пишутся слитно и каждое новое начинается с заглавной буквы. Например: numberOfPeople. Запись получается «бугристая» и похожа на горбы верблюда, отсюда и появилось такое название.

Существует ряд особенностей:

  • имена классов (не объектов, а именно классов. Объект — это поле) всегда пишутся с Заглавной буквы;
  • первая буква в имени поля всегда маленькая;
  • в языке C# первая буква имени метода всегда Заглавная;
  • в языке Java первую букву имени метода всегда принято писать маленькой.

Вот мы и рассмотрели реализацию класса в ООП. Надеемся, что данный материал помог вам разобраться в этой теме.

15.1. Классы и объекты. Поля и методы

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

Объект — это экземпляр класса.

В PHP каждое определение класса начинается с ключевого слова class, затем следует имя класса, и далее пара фигурных скобок, которые заключают в себе определение свойств и методов этого класса.

Именем класса может быть любое слово, при условии, что оно не входит в список зарезервированных слов PHP, начинается с буквы или символа подчёркивания и за которым следует любое количество букв, цифр или символов подчёркивания.

Пример

<?php
class SimpleClass
{
    // объявление свойства
    public $var = 'значение по умолчанию';
    
    private $secret = 'secret';//доступно только внутри класса 

    // объявление метода
    public function displayVar() {
        echo $this->var;
    }
}
<?php
$instance = new SimpleClass();

Five Principles for Object-Oriented Programming

When performing object-oriented programming, there is a set of five core design principles that are recommended for creating flexible, maintainable architectures known as the SOLID principles:

•    Single-responsibility principle
•    Open-closed principle
•    Liskov substitution principle
•    Interface segregation principle
•    Dependency inversion principle

Dependency Inversion Principle

What I want to point attention to here is the dependency inversion principle.

The dependency inversion principle states that we should depend on abstractions, not implementations. What does that mean?

If we are only worried about vocalization, we should not need to worry about whether or not we have a Bird, a Human, or an Animal. We should worry about whether or not we have something capable of vocalization. 

Interfaces allow us to define these capabilities, and then allow our code to typehint against those capabilities, and not a more specific type. This in turn allows us to substitute different types when they fulfill the contract defined by the interface.

Common Mistake When Performing Object-Oriented Programming

One common mistake when beginning to perform object-oriented programming is to create a strict inheritance tree: a base class, and then subtypes of that base class, and then implementations of those subtypes, and so on. 

This can lead to creating a class that technically has a dozen or more different behaviors, but which is only used for one of them. By splitting these behaviors out into different interfaces, we can create classes that implement only specific behaviors, and use them wherever those behaviors are needed.

Рейтинг
( Пока оценок нет )
Editor
Editor/ автор статьи

Давно интересуюсь темой. Мне нравится писать о том, в чём разбираюсь.

Понравилась статья? Поделиться с друзьями:
Люкс-хост
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: