За последние 24 часа нас посетили 44608 программистов и 3002 робота. Сейчас ищут 1306 программистов ...

Использование интерфейсов в ооп

Тема в разделе "PHP для новичков", создана пользователем Dimon2x, 11 апр 2017.

  1. Dimon2x

    Dimon2x Старожил

    С нами с:
    26 фев 2012
    Сообщения:
    2.211
    Симпатии:
    186
    Правильно ли я понял, что интерфейс можно использовать тогда, когда в конкретный класс, надо поместить все методы, которые нужны, из других классов, но так не получится, потому что класс наследуется, только от одного класса.

    А когда есть несколько интерфейсов и в них разные методы, то эти методы, можно пораскидать по разным классам, а потом уже унаследовать все нужные методы из разных классов, через implements и названия интерфейсов.
     
  2. romach

    romach Старожил

    С нами с:
    26 окт 2013
    Сообщения:
    2.904
    Симпатии:
    719
    Интерфейс - это контракт. Если у вас есть несколько разных классов, которые делают одно и тоже, но разными способами, то вам нужны интерфейсы. Типичный пример - это работа с файловой системой:
    1. ФС может быть физически расположена на той же машине (либо примонтирована) и работать с ней можно обычными способами ОС.
    2. ФС может быть удаленной, лежать где-нибудь на S3 или доступна только по sftp
    3. Что-то ещё, возможно даже экзотическое.
    В таких случаях вы создаете интерфейс, где описаны публичные методы (название, что передает, что возвращает) и каждой реализации его имплементируете. Важно понимать, что сам по себе он не делает ничего, он нужен лишь для уверенности программиста в том, что вот эта вот реализация работает внешне точно так, у неё есть определенный набор методов и подменив какой-нибудь LocalFS на SFTPFS не придется переписывать половину проекта.

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

    А то про что вы писали судя по всему - трейты )
     
    denis01 и Dimon2x нравится это.
  3. Dimon2x

    Dimon2x Старожил

    С нами с:
    26 фев 2012
    Сообщения:
    2.211
    Симпатии:
    186
    одно и тоже, но разными способами, можно небольшой пример?
     
  4. romach

    romach Старожил

    С нами с:
    26 окт 2013
    Сообщения:
    2.904
    Симпатии:
    719
    https://github.com/thephpleague/flysystem/blob/master/src/AdapterInterface.php
    https://github.com/thephpleague/flysystem/tree/master/src/Adapter

    ну или совсем на пальцах:

    PHP:
    1. <?php
    2.  
    3. interface MultiplicateThis
    4. {
    5.     public function multiplicate(int $a, int $b) : int;
    6. }
    7.  
    8. class Addition implements MultiplicateThis
    9. {
    10.     public function multiplicate(int $a, int $b): int
    11.     {
    12.         $response = 0;
    13.         for($i = 1; $i <= $b; $i++) {
    14.             $response += $a;
    15.         }
    16.  
    17.         return $response;
    18.     }
    19. }
    20.  
    21. class Multiplication implements MultiplicateThis
    22. {
    23.     public function multiplicate(int $a, int $b): int
    24.     {
    25.         return $a * $b;
    26.     }
    27. }
    28.  
    29. class Test
    30. {
    31.     protected $adapter;
    32.  
    33.     public function __construct(MultiplicateThis $adapter)
    34.     {
    35.         $this->adapter = $adapter;
    36.     }
    37.  
    38.     public function run()
    39.    {
    40.         $a = rand(0, 100);
    41.         $b = rand(0, 100);
    42.         echo "a: $a".PHP_EOL;
    43.         echo "b: $b".PHP_EOL;
    44.         $result = $this->adapter->multiplicate($a, $b);
    45.         echo "result: $result".PHP_EOL;
    46.         if ($result === $a * $b) {
    47.             echo "yeah!".PHP_EOL;
    48.         } else {
    49.             echo "wtf?".PHP_EOL;
    50.         }
    51.     }
    52. }
    53.  
    54. (new Test(new Addition()))->run();
    55. (new Test(new Multiplication()))->run();
     
    Dimon2x нравится это.
  5. Dimon2x

    Dimon2x Старожил

    С нами с:
    26 фев 2012
    Сообщения:
    2.211
    Симпатии:
    186
    Правильно ли я использовал интерфейс?

    PHP:
    1. <?php
    2. header('Content-Type: text/html; charset=utf-8');
    3. interface GetPriceProduct{
    4.     public function getPrice();
    5. }
    6. class Product{
    7.     protected $name;
    8.     protected $price;
    9.  
    10.     function __construct($name, $price){
    11.         $this->name = $name;
    12.         $this->price = $price;
    13.     }
    14. }
    15. class Pasta extends Product implements GetPriceProduct{
    16.     public function getPrice(){
    17.         return $this->price;
    18.     }
    19. }
    20. $makaron = new Pasta('Макароны', 200);
    21. echo $makaron->getPrice();
    --- Добавлено ---
    то есть интерфейс удобно использовать тогда, когда метод принимает одинаковое количество аргументов и делает с ними разные вещи?
    --- Добавлено ---
    В вашем примере, почему-то ругается на двоеточие

    PHP:
    1. public function multiplicate(int $a, int $b) : int;
     
  6. romach

    romach Старожил

    С нами с:
    26 окт 2013
    Сообщения:
    2.904
    Симпатии:
    719
    Скорее когда делает одни и те же вещи, но разными способами. Важно что всю логику ты прячешь где-то там, за интерфейсом, который знает остальная часть приложения и работает исключительно с ним. Будут ли $a и $b сразу перемножаться, складываться или стучаться в гугл за ответом - вопрос реализации, приложение же знает, что если класс реализует этот интерфейс, то всегда можно дернуть метод с двумя целыми числами и получить результат.

    В целом - да, правильно. Теперь в том месте, где нужно работать с ценой товара, ты можешь просто проверить на интерфейс и не важно, паста это или отбойный молоток, хранится ли цена в базе или дергается из xml скаченного с яндекс-маркета. Тут главное не переборщить и не лепить интерфейсы куда попало. Это хелпер, что бы случайно не поменять что-то, от чего отвалится половина приложения. Изменил метод, который описан в интерфейсе, пых сразу отвалился с ошибкой и сиди думай что дальше делать ))
    --- Добавлено ---
    это в PHP7 добавили.
     
  7. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.600
    Симпатии:
    1.764
    Интерфейс можно рассматривать как абстрактный класс без полей, и без реализованных методов. Плюс интерфейсы в некотором роде замена множественного наследования - т.е. класс может реализовывать несколько интерфейсов. В твоём примере ты вообще никак не используешь преимущества от интерфейсов. Это всё в продолжении темы полиморфизма, которую ты не чувствуешь явно. Вот если брать даже твой пример:

    PHP:
    1. interface HasPrice {
    2.       function getPrice();
    3. }
    4.  
    5. class Product implements HasPrice {
    6.     private $price;
    7.     function __construct($price) {$this->price = $price }
    8.     function getPrice()
    9.     {
    10.           return $this->price;
    11.     }
    12. }
    13.  
    14. class Collection implements HasPrice {
    15.      /** @var HasPrice[] */
    16.      private $products;
    17.      /**
    18.      * @param HasPrice[] $products
    19.      */
    20.      public function __construct(array $products)
    21.      {
    22.             $this->products = $products;    
    23.      }
    24.  
    25.      public function getPrice()
    26.      {
    27.             $sum = 0;
    28.             foreach ($this->products as $p) {
    29.                $sum += $p->getPrice();
    30.            }
    31.            return $sum;
    32.      }
    33. }
    34.  
    35. function printPrice(HasPrice $product)
    36. {
    37.      echo "Цена " . $product->getPrice() . "<br>";
    38. }
    39.  
    40. $p1 = new Product(12);
    41. $p2 = new Product(25);
    42. $p3 = new Product(57);
    43. $c1 = new Collection([$p1, $p2]);
    44. $c2 = new Collection([$c1, $p3]);
    45. printPrice($p1);
    46. printPrice($c1);
    47. printPrice($c2);
    Потому что у тебя не седьмой php
     
  8. Dimon2x

    Dimon2x Старожил

    С нами с:
    26 фев 2012
    Сообщения:
    2.211
    Симпатии:
    186
    если какой-нибудь пример кода одной задачи, которая решена с помощью интрефейсов, а друга без? и увидеть в чём же приемущество
     
  9. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.600
    Симпатии:
    1.764
    @Dimon2x, я же тебе показал вполне нормальный код. Хотя короткий пример очень трудно придумать. Вот посмотри мой код. Функция printPrice() понятия не имеет, какой объект в неё будет передан. Главное, чтоб он реализовывал интерфейс HasPrice, в котором определён метод getPrice(). В неё я передаю сначала экземпляр Product, в котором getPrice() просто возвращает значение поля price(), а потом передаю экземпляр Collection, где getPrice() уже делает куда больше - суммирует все объекты в коллекции. Это один пример использования интерфейсов.

    Второй пример - это класс Collection. В нём можно объединять экземпляры любых классов, реализующих HasPrice, и он будет считать сумму. Его функция getPrice() вызывает getPrice() объектов внутри коллекции и суммирует их. Обрати внимание, что каждый элемент коллекции может быть экземпляром любого класса, реализующего HasPrice, в том числе и другой коллекцией, как в объекте $c2. Смысл в том, чтобы писать функции, которые не зависят от конкретной реализации класса с ценой, они лишь должны содержать реализацию интерфейса. Сюда можно было добавить ещё класс Service, описывающий услугу, и тогда можно было бы описывать коллекции из услуг и товаров. И функция printPrice() всегда будет верно выводить цену (при условии, что верна реализация getPrice()).
     
    Dimon2x нравится это.
  10. Fell-x27

    Fell-x27 Суперстар
    Команда форума Модератор

    С нами с:
    25 июл 2013
    Сообщения:
    12.156
    Симпатии:
    1.771
    Адрес:
    :сердА
    Автор, гляди. Есть вот Камаз, Жигули и Болид F1. Это крайне разные машины. По-разному устроены. Для разных целей сделаны. Отличия в характеристиках колоссальные. Но...у них есть общий интерфейс вида "руль,педали". И человеку, в общем-то не надо знать, как сделана та или иная машина. Он знает интерфейс "руль, педали", и знает, что если крутить рулем, машина будет входить в поворот. А педали отвечают за ускорение и торможение. Грубенько, но суть дает.

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

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