Статьи

Шаблон декоратора в Eclipse Modeling Framework

В предыдущей статье я обсуждал, как создавать универсальные классы в Eclipse Modeling Framework (EMF). В этой статье я расскажу, как использовать эти знания для реализации шаблона декоратора в EMF. В процессе, мы будем продвигать наши знания дженериков немного дальше.

В некоторых случаях полезно или даже необходимо обернуть унаследованные классы Java, особенно Java-бины, в модель EMF. Например, вам может потребоваться отобразить информацию, предоставленную службой удаленного вызова методов Java (RMI). У вас нет доступа к исходному коду, только файлы JAR.

Я лично полагаюсь на EMF для управления отображением объектов в EMF. Честно говоря, кто хочет написать весь этот код в рамках EMF.Edit? Я бы предпочел щелкнуть правой кнопкой мыши на файле GENMODEL и двигаться дальше.
Я создал пример упаковки или декорирования на заданных объектах другим. В этом примере у нас есть устаревший интерфейс Java с именем Aramaic. Мы создадим модель EMF с именем Greek, которая обернет классы от арамейского.
В листинге 1 показаны арамейские интерфейсы. Я сохранил это как можно проще. Класс Алеф имеет атрибут фамилия. Класс Beth наследуется от Aleph и добавляет псевдоним атрибута.

package com.jeffreyricker.aramaic;

public interface Aleph {
    public String getSurname();
}

public interface Beth extends Aleph {
    public String getNickname();
}

Листинг 1

Предположим, что мы передали JAR этой реализации. Мы бы использовали мастер плагинов Eclipse для преобразования JAR в пакет OSGI.
Далее нам нужно создать нашу модель EMF, которая будет обернуть или украсить объекты, которые реализуют арамейские интерфейсы. Я хочу сохранить это как можно более чистым. Интерфейс EMF должен отражать, но не иметь ссылки на устаревшие интерфейсы. Результат показан в листинге 2.

/** 
 * @model 
 */
public interface Alpha {

	/** 
       * @model 
       */
	public String getName();
}

/** 
 * @model
 */
public interface Beta extends Alpha {

	/**
	 * @model
	 */
	public String getNickname();

}

Перечисление 2 

Фактическая упаковка скрыта в реализации. В греческих интерфейсах нет ссылок на арамейские классы.
Нам нужен базовый интерфейс для украшения. Вот где начинаются дженерики. Интерфейс Decorator, показанный в листинге 3, очень прост. Универсальный <D> — это класс, который будет упакован декоратором. Реализация интерфейса показана в листинге 4.

package com.jeffreyricker.greek.util;

public interface Decorator<D> {
    
     D getDecorated();
    
     void setDecorated(D decorated);

}

Перечисление 3
 

package com.jeffreyricker.greek.impl;

import org.eclipse.emf.ecore.impl.EObjectImpl;
import com.jeffreyricker.greek.util.Decorator;

public class DecoratorImpl<D> extends EObjectImpl implements Decorator<D>{

    private D decorated;
    
    public D getDecorated() {
        return decorated;
    }

    public void setDecorated(D decorated) {
        this.decorated = decorated;
    }

}
 

Листинг 4

 

Наши греческие модельные объекты будут наследоваться от этого класса DecoratorImpl. Первый декоратор Alpha показан в листинге 5.

package com.jeffreyricker.greek.impl;

import com.jeffreyricker.aramaic.Aleph;
import com.jeffreyricker.greek.Alpha;

public class AlphaImpl<D extends Aleph> 
    extends DecoratorImpl<D> implements Alpha{

    public AlphaImpl(){
        super();
    }
    
    public String getName() {
        return getDecorated().getSurname();
    }

}

Листинг 5

Обратите внимание, что метод getName выполняет вызов getDecorated (). GetSurname (). Дженерики делают это возможным. Объявление <D extends Aleph> позволяет анализатору знать, что класс, возвращаемый getDecorated, будет иметь метод getSurname.
Объявление класса может выглядеть немного странно.

class AlphaImpl<D extends Aleph> extends DecoratorImpl<D> implements Alpha

Типичная декларация, которую можно ожидать:

class AlphaImpl extends DecoratorImpl<Aleph> implements Alpha
// OKAY, BUT NO INHERITANCE

Это, безусловно, будет работать для этого класса. Однако, что происходит, когда нам нужно наследовать BetaImpl?

class BetaImpl extends AlphaImpl<Beth> implements Beta
// WRONG

Если вы попробуете это, вы получите сообщение об ошибке, в котором совершенно правильно указано, что AlphaImpl не является универсальным.
Вы можете попробовать следующее

class BetaImpl extends DecoratorImpl<Beth> implements Beta
// WRONG
It would compile, but then the following would fail.
Beta beta = new BetaImpl();
beta.getName();
// FAIL. No such method
The following also do not work. 
class AlphaImpl<D> extends DecoratorImpl<D> implements Alpha
// WRONG
class AlphaImpl<C extends D> extends DecoratorImpl<Aleph> implements Alpha
// WRONG

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

package com.jeffreyricker.greek.impl;

import com.jeffreyricker.aramaic.Beth;
import com.jeffreyricker.greek.Beta;

public class BetaImpl<D extends Beth> extends AlphaImpl<D> implements Beta{

    public String getNickname(){
        return this.getDecorated().getNickname();
    }

}

Листинг 6

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

С http://www.jeffreyricker.com/