Врожденный «порок вхождения»

AmdY

Пью пиво
Команда форума
Ай, типичные хабромудаки, вместо контраргументов придрались к опечатке. Я согласен с Casus, php теряет своё преимущество на простых задачах, а новый функционал строится на костылях, шедевр - это strict_types.
bolk - очень даже адекватный, не ожидал от него такого.
 

Вурдалак

Продвинутый новичок
http://habrahabr.ru/post/258139/#comment_8423963
Как вам parent?
Код:
namespace Bar;
interface Foo {}
interface FooManager {
public function bind():Foo;
}
class FooImpl implements Foo {}
class FooManagerImpl implements FooManager {
public function bind():FooImpl {
return new FooImpl();
}
}
//Fatal error: Declaration of Bar\FooManagerImpl::bind() must be compatible with Bar\FooManager::bind(): Bar\Foo in *** on line 14
LSP? Не, не слышал.
 

fixxxer

К.О.
Партнер клуба
Хабр такой хабр. :)

Впрочем, отсутствие nullable return types - не ок
 

Вурдалак

Продвинутый новичок
Да, но по правде сказать, я, например, предпочитаю @throws FooNotFound, чем @return Foo|null.
 

fixxxer

К.О.
Партнер клуба
Не, ну я понял, что он хочет:

PHP:
interface Foo {
    public int getId();
}
interface FooManager {
    public Foo bind();
}
class FooImpl implements Foo {
    public int getId() {
        return 1;
    }
    public int getBar() {
        return 2;
    }
}
class FooManagerImpl implements FooManager {
    public FooImpl bind() {
        return new FooImpl();
    }
}

class Test {
    public static void main(String[] args) {
        FooManager fm = new FooManagerImpl();
        Foo foo = fm.bind();
        System.out.println(foo.getId());
        // System.out.println(foo.getBar()); - тут будет cannot find symbol,
        //    а если двумя строчками выше объявить FooImpl foo - будет incompatible types

        FooManagerImpl fm2 = new FooManagerImpl();
        FooImpl foo2 = fm2.bind();
        System.out.println(foo2.getId());
        System.out.println(foo2.getBar()); // а так сработает
    }
}
В php так фиг сделаешь.
 

WMix

герр M:)ller
Партнер клуба
тут уж надо определиться что ты хочешь, не?
PHP:
interface FooManager {
/*public Foo bind():Foo; */ // ну, тогда это не часть интерфейса и опять все работает
}
 

fixxxer

К.О.
Партнер клуба
Не, его логика в том, что часть интерфейса как раз.

Вот так наверное понятнее
PHP:
interface Foo {
    public int getId();
}
interface FooManager {
    public Foo bind();
}
class FooImpl implements Foo {
    public int getId() {
        return 1;
    }
    public int getBar() {
        return 10;
    }
}
class FooImpl2 implements Foo {
    public int getId() {
        return 2;
    }
    public int getBaz() {
        return 20;
    }
}
class FooManagerImpl implements FooManager {
    public FooImpl bind() {
        return new FooImpl();
    }
}
class FooManagerImpl2 implements FooManager {
    public FooImpl2 bind() {
        return new FooImpl2();
    }
}

class Test {
    private static void test(FooManager fm) {
        Foo foo = fm.bind();
        System.out.println(foo.getId());
    }

    public static void main(String[] args) {
        FooManagerImpl fm = new FooManagerImpl();
        FooImpl foo = fm.bind();
        System.out.println(foo.getBar());

        FooManagerImpl2 fm2 = new FooManagerImpl2();
        FooImpl2 foo2 = fm2.bind();
        System.out.println(foo2.getBaz());

        test(fm);
        test(fm2);
    }
}
LSP так не нарушается.
 

Lionishy

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

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

Широко используется в Java/C#/C++ .
 

WMix

герр M:)ller
Партнер клуба
у меня нет php7 чтоб проверить а 5.6 не то чтоб кастит
PHP:
<?php

interface A {
  public function b();
}

class X implements A{
  public function b(){
  echo "hallo";
  }
  public function c(){
  echo "world";
  }

}

class Test{
  private $a;
  public function setA( A $a ){
    $this->a = $a; // это все одно class X
  }
  public function fetch(){
    echo $this->a->b();
    echo " ";
    echo $this->a->c();
  }
}

$test = new Test;
$test->setA( new X );
$test->fetch();
 

WMix

герр M:)ller
Партнер клуба
и типа такого ( (X)$this->a )->c(); тоже нельзя?
 

Vladson

Сильнобухер
А помоему правда, вхождение в язык РНР это очень низкий порок :)

Ай, типичные хабромудаки, вместо контраргументов придрались к опечатке. Я согласен с Casus, php теряет своё преимущество на простых задачах, а новый функционал строится на костылях
Вот тут полностью согласен.
 

Вурдалак

Продвинутый новичок
LSP так не нарушается.
Говнокод в любом случае. Я бы сказал, что тут ещё нарушается DIP. Если где-то нужно напрямую юзать именно FooManagerImpl и FooManagerImpl2, то здесь что-то не так. А по поводу LSP оно может быть неявно: если требуется указывать конкретную реализацию, то не означает ли это, что эта реализация чем-то отличается от контракта? Например, неявный контракт в виде описания исходного интерфейса: «этот метод возвращает положительные числа», а конкретная реализация возвращает и отрицательные.

Ну и как бэ PHP. Тут-то контракты никто не соблюдает, потому что компилятора на них нет. Я подозреваю, что этот чувак с хабра о таких вещах и не задумывается.
 
Последнее редактирование:

WMix

герр M:)ller
Партнер клуба
мне тоже кажется, вопрос как интерпретировать public function a():<interface>,
<interface> это "как минимум" или "не больше чем"?
 

fixxxer

К.О.
Партнер клуба
Говнокод в любом случае
Придумать не говнокод у меня не получается, да.

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

Lionishy

Новичок
( (X)$this->a )->c(); тоже нельзя?
Если полагаться на рефлексию, то зачем вообще полагаться на возвращаемый тип? Вопрос риторический. Не нужно указывать возвращаемый тип, следует полагаться на динамическую проверку, которая установит соответствие реально возвращаемого типа ожидаемому или провалится с каким-нибудь страшным исключением.

По поводу LSP. Если FooImpl -- подтип Foo, то принцип подстановки не нарушится на утверждениях, содержащих только выражения над FooManager::bind().

строгая типизация при отсутствии объявления типов переменных
Можно считать, что переменной присваивается тот тип, который имеет выражение в правой части.
 
Сверху