Как вы обходите необходимость приводить интерфейсный объект обратно в его базовый класс? - PullRequest
1 голос
/ 09 мая 2009

Этот вопрос предназначен для применения к интерфейсам в целом, но я буду использовать AS3 / Flex для моего языка. Должно быть [в основном] очевидно, как применять его на разных языках.

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

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

Вот пример интерфейса:

public interface IFooable extends IUIComponent {
    function runFoo():void;
}

Допустим, я создаю базовый класс, который расширяет VBox и реализует интерфейс:

public class Foo extends VBox implements IFooable {
    public Foo() {
        super();
        //stuff here to create Foo..blah blah
    }
    public function runFoo():void {
        // do something to run foo
    }
}

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

public function init():void {
    var foo:IFooable = new Foo();
    foo.percentHeight = 100; //works because of IUIComponent
}

Но , если я хочу добавить Foo в контейнер приложения, мне теперь нужно привести его обратно к базовому классу (или к другому базовому классу):

public function init():void {
    var foo:IFooable = new Foo();
    foo.percentHeight = 100;

    addChild(foo as DisplayObject); //_have_ to cast, because addChild takes a 'DisplayObject' class type

    //could also do this:
    //addChild(foo as VBox);
}

Не было ли первоначальное намерение скрывать реализацию Foo? Существует предположение, что Foo является объектом DisplayObject. К сожалению, возможность добавить пользовательский объект в контейнер кажется невозможной без приведения.

Я что-то упустил полностью? Это действительно просто феномен в Flex / AS3? Если у вас есть контейнер в базовом API языка, и он позволяет вам только добавлять дочерние элементы определенного типа класса, как вы тогда абстрагируете реализацию?

Для записи, этот вопрос , кажется, задает вопрос, возможна ли такая операция , возможна , но на самом деле не рассматривается, почему это может быть плохой дизайн (и как исправить это).


2-я мысль:

Абстрактные классы :

Как указал Мэтью, абстрактные классы помогают решить некоторые из этих задач: я мог бы создать базовый абстрактный класс, который наследуется от DisplayObject (или, в моем случае, VBox, поскольку он является дочерним по отношению к DisplayObject), и иметь Базовый класс реализует интерфейс. Таким образом, любой класс, который расширяет абстрактный класс, будет тогда необходим для реализации в нем методов.

Отличная идея - но AS3 не имеет абстрактных классов (насколько я знаю).

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

Это все же решение.


Контекст

Некоторый контекст может помочь:

У меня есть приложение, которое может иметь любое количество субконтейнеров. Каждый из этих субконтейнеров будет иметь свои собственные соответствующие параметры конфигурации, параметры и т. Д. Однако само приложение имеет глобальный ApplicationControlBar, который будет содержать меню точки входа для доступа к этим параметрам конфигурации. Поэтому всякий раз, когда я добавляю подкомпонент в основное приложение (через «addChild»), он также «регистрирует» свои собственные параметры конфигурации в меню ApplicationControlBar. Это сохраняет знания о конфигурируемости самих контейнеров, но позволяет использовать более унифицированные средства доступа к ним.

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

Ответы [ 4 ]

2 голосов
/ 10 мая 2009

@ Джеймс Уорд, Это определенно то, что я хотел бы сказать на языке, вероятно, интерфейс IDisplayObject. Это решило бы много проблем в программировании дисплея ООП в AS3.

Что касается исходного вопроса, то, что я использовал в прошлом и упомянул на сайте www.as3dp.com, - это включение метода getDisplay (): DisplayObject в интерфейс, который обычно возвращает «this» его разработчиком. Это не идеально, но работает.

@ Matthew Flaschen, хотя у нас нет классов Abstarct, родных для AS3, обычной практикой является присвоение имени классу слова Abstract, т.е. AbstarctMyObject, а затем просто обрабатывать его как объекты abstarct в Java и других языках. Наша потребность в настоящих классах abstarct - это то, о чем хорошо знает команда Flash Player, и мы наверняка увидим это в следующей версии языка ActionScript.

1 голос
/ 09 мая 2009

Я думаю, что это то место, где API Flex / Flash некорректен. Я думаю, что addChild должен брать интерфейс, а не класс. Однако, поскольку это не тот случай, вы должны разыграть его. Другим вариантом будет обезьяна заплатить UIComponent, чтобы он взял интерфейс, или, возможно, добавить другой метод, например addIChild(IUIComponent). Но это грязно. Поэтому я рекомендую сообщить об ошибке .

1 голос
/ 09 мая 2009

Хорошо, я вообще отвечаю, потому что вы сказали: «Это действительно просто феномен в Flex / AS3?».

В вашем методе init очевидно, что вы всегда вызываете addChild с помощью foo. Это означает, что foo всегда должен быть экземпляром DisplayObject. Вы также хотите, чтобы это был экземпляр IFooable (хотя здесь непонятно, почему). Поскольку DisplayObject является классом, вы должны рассмотреть возможность использования подкласса DisplayObject (например, FooableDisplayObject), который реализует IFooable. На Java это будет ниже. Я не знаком с AS, но думаю, что здесь нет никаких общих недостатков в интерфейсах.

interface IFooable
{
    public void runFoo();
}

class DisplayObject
{

}

abstract class FooableDisplayObject extends DisplayObject implements IFooable
{

}

class Foo extends FooableDisplayObject
{
    public void runFoo()
    {

    }
}

public void init()
{
    FooableDisplayObject foo = new Foo();
    foo.percentHeight = 100;

    addChild(foo);
}
0 голосов
/ 19 января 2011

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

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

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

Начало проектирования реализации. Нужно просто использовать интерфейс для специализированного материала и расширить подкласс от того места, к которому он в основном принадлежит. Теперь страница имеет «базовое» значение, отображаемое (мудрый дизайн… base'-класс является объектом отображения), но может потребоваться некоторая специализация, для которой создается интерфейс для этого.

        public class Page extends Sprite{...}
        public interface IPageLoader{ function loadPage():void{}; function initPage():void{}; }
        public class Homepage extends Page implements IPageLoader
        { function loadPage():void{/*do stuff*/}; function initPage():void{/*do stuff*/}; }

        var currentpage:Page;
        var currentpageLoader:IPageLoader;

        currentpage = new Homepage;
        currentpageLoader = currentpage as IPageLoader;

        currentpageLoader.loadPage();
        currentpageLoader.initPage();

        addChild(currentpage);
        Tween(currentpage, x, CENTER);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...