SDA SE Wiki

Software Engineering for Smart Data Analytics & Smart Data Analytics for Software Engineering

User Tools

Site Tools


Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
research:cultivate:refactoringtoaspectsexamplenomadpimidateservicelistener [2009/09/17 21:11]
Daniel Speicher
research:cultivate:refactoringtoaspectsexamplenomadpimidateservicelistener [2018/05/09 01:59] (current)
Line 1: Line 1:
  
 +===== Context =====
 +
 +
 +Nomad PIM (in the current, not-yet-released version) uses a service that returns the current date context of the application and notifies interest listeners on changes. The idea of that is that all date related information like the one in schedule views, calendar, title bar, evaluation views is refreshed based on the currently selected date. For example, if you choose a certain date in the calendar view, the schedule view should show only information for that date (or a timespan that date lies in, like a week or a month) and the title bar should be refreshed accordingly.
 +
 +
 +===== State before the refactoring =====
 +
 +==== Calling Code and Interface ====
 +
 +
 +For the refactoring,​ I will restrict myself to the views that implement IDateServiceListener. These views are called on changes by the following code snippet:
 +
 +
 +
 +<Code lang-java>​
 +        IViewReference[] viewReferences = PlatformUI.getWorkbench()
 +                .getActiveWorkbenchWindow().getActivePage().getViewReferences();​
 +
 +        for (IViewReference reference : viewReferences) {
 +            IViewPart view = reference.getView(false);​
 +            if (view != null) {
 +                final IDateServiceListener adapter = (IDateServiceListener) view
 +                        .getAdapter(IDateServiceListener.class);​
 +
 +                if (adapter != null) {
 +                    Display.getCurrent().asyncExec(new Runnable() {                    ​
 +                        public void run() {
 +                            adapter.dateChange(newDate,​ source);
 +                        }                    ​
 +                    });
 +                }
 +            }
 +        }
 +</​Code>​
 +
 +
 +
 +Basically, it searchs for all currently available views that implement IDateServiceListener either directly or indirectly (using adapter factories). If they implement the interface directly, adapter == view holds because of the implementation of getAdapter().
 +
 +
 +=== Interface IDateServiceListener ===
 +
 +
 +<Code lang-java>​
 +
 +
 +
 +public interface IDateServiceListener {
 +
 +
 +
 +    void dateChang
 +
 +(Date newDate, Object source);
 +
 +    ​
 +
 +}
 +
 +</​Code>​
 +
 +
 +==== Views ====
 +
 +
 +Here is a short list of the views that implement IDateServiceListener:​
 +
 +
 +  * CalendarView
 +  * DayView
 +  * PastEventsView
 +  * ScheduleView
 +  * TimeEvaluationView
 +  * WeekOverviewView
 +
 +The implementations all differ, many have weaknesses caused by the inconsistent implementation of time. For example, some views have link buttons to let the user select whether they should be updated on changes or not, whereas other views do not have such a button.
 +
 +
 +
 +The refactoring should also improve the quality of the code by eliminating these weaknesses (one could perhaps argue that it's therefore not really a refactoring).
 +
 +
 +=== Relevant code from TimeEvaluationView ===
 +
 +
 +<Code lang-java>​
 +
 +
 +
 +public class TimeEvaluationView extends ViewPart implements
 +
 +        IDateServiceListener {
 +
 +
 +
 +    private Date currentDate;​
 +
 +
 +
 +    private LinkAction linkAction;
 +
 +
 +
 +    public void dateChange(Date newDate, Object source) {
 +
 +        if (currentDate != null
 +
 +                && new SameWeekFilter(currentDate).evaluate(newDate)) {
 +
 +            return;
 +
 +        }
 +
 +        ​
 +
 +        if (!linkAction.isChecked()) {
 +
 +            return;
 +
 +        }
 +
 +
 +
 +        currentDate = newDate;
 +
 +
 +
 +        refresh();
 +
 +    }
 +
 +
 +
 +...
 +
 +
 +
 +}
 +
 +</​Code>​
 +
 +
 +
 +
 +
 +
 +=== Relevant code from ScheduleView ===
 +
 +
 +<Code lang-java>​
 +
 +
 +
 +public class ScheduleView extends ViewPart implements
 +
 +        IDateServiceListener {
 +
 +
 +
 +    private LinkAction linkAction;
 +
 +
 +
 +    public void dateChange(Date newDate, Object source) {
 +
 +        if (!linkAction.isChecked()) {
 +
 +            return;
 +
 +        }
 +
 +
 +
 +        getAction(SCHEDULE_VIEW_FILTER_DAY).changeFilter(
 +
 +                new DateFilterToInformationFilterAdapter(new SameDayFilter(
 +
 +                        newDate)));
 +
 +        getAction(SCHEDULE_VIEW_FILTER_WEEK).changeFilter(
 +
 +                new DateFilterToInformationFilterAdapter(new SameWeekFilter(
 +
 +                        newDate)));
 +
 +        getAction(SCHEDULE_VIEW_FILTER_MONTH).changeFilter(
 +
 +                new DateFilterToInformationFilterAdapter(new SameMonthFilter(
 +
 +                        newDate)));
 +
 +    }
 +
 +
 +
 +...
 +
 +
 +
 +}
 +
 +
 +
 +</​Code>​
 +
 +
 +=== Relevant code from DayView ===
 +
 +
 +<Code lang-java>​
 +
 +
 +
 +public class DayView extends ViewPart implements
 +
 +        IDateServiceListener {
 +
 +
 +
 +    public void dateChange(Date newDate, Object source) {
 +
 +        setModel(DiaryUtil.getDay(newDate));​
 +
 +    }
 +
 +
 +
 +...
 +
 +
 +
 +}
 +
 +
 +
 +</​Code>​
 +
 +
 +
 +
 +
 +
 +=== Relevant code from WeekOverviewView ===
 +
 +
 +<Code lang-java>​
 +
 +
 +
 +public class WeekOverviewView extends ViewPart implements
 +
 +        IDateServiceListener {
 +
 +
 +
 +    private Date date;
 +
 +
 +
 +    private LinkAction linkAction;
 +
 +
 +
 +    public void dateChange(Date newDate, Object source) {
 +
 +        setDate(newDate);​
 +
 +    }
 +
 +
 +
 +    private void setDate(Date newDate) {
 +
 +        assert newDate != null;
 +
 +
 +
 +        if (!linkAction.isChecked()) {
 +
 +            return;
 +
 +        }
 +
 +        ​
 +
 +        Date newWeekDate = dateConverter.convert(newDate);​
 +
 +
 +
 +        if (newWeekDate.equals(date)) {
 +
 +            return;
 +
 +        }
 +
 +        ​
 +
 +        date = newWeekDate;​
 +
 +
 +
 +        viewer.setInput(date);​
 +
 +    }
 +
 +
 +
 +...
 +
 +
 +
 +}
 +
 +
 +
 +</​Code>​
 +
 +
 +=== Relevant code from CalendarView ===
 +
 +
 +<Code lang-java>​
 +
 +
 +
 +public class TimeEvaluationView extends ViewPart implements
 +
 +        IDateServiceListener {
 +
 +
 +
 +    public void dateChange(Date newDate, Object source) {
 +
 +        if (source == this) {
 +
 +            return;
 +
 +        }
 +
 +
 +
 +        Calendar calendar = Calendar.getInstance();​
 +
 +        calendar.setTime(newDate);​
 +
 +        swtCalendar.setCalendar(calendar);​
 +
 +    }
 +
 +
 +
 +...
 +
 +
 +
 +}
 +
 +
 +
 +</​Code>​
 +
 +
 +=== Relevant code from PastEventsView ===
 +
 +
 +<Code lang-java>​
 +
 +
 +
 +public class PastEventsView extends ViewPart implements
 +
 +        IDateServiceListener {
 +
 +
 +
 +    private Date date = new Date();
 +
 +
 +
 +    public void setDate(Date newDate) {
 +
 +        this.date = newDate;
 +
 +        update();
 +
 +    }
 +
 +
 +
 +...
 +
 +
 +
 +}
 +
 +
 +
 +</​Code>​
 +
 +
 +===== Steps =====
 +
 +==== 1. Introduce a tagging annotation ====
 +
 +=== create the annotation ===
 +
 +
 +<Code lang-java>​
 +
 +
 +
 +import java.lang.annotation.Documented;​
 +
 +import java.lang.annotation.ElementType;​
 +
 +import java.lang.annotation.Retention;​
 +
 +import java.lang.annotation.RetentionPolicy;​
 +
 +import java.lang.annotation.Target;​
 +
 +
 +
 +@Retention(RetentionPolicy.RUNTIME)
 +
 +@Target( { ElementType.TYPE })
 +
 +@Documented
 +
 +public @interface DateServiceUser {
 +
 +    ​
 +
 +}
 +
 +
 +
 +</​Code>​
 +
 +
 +=== apply it to the views (example) ===
 +
 +
 +<Code lang-java>​
 +
 +
 +
 +@DateServiceUser
 +
 +public class TimeEvaluationView extends ViewPart implements
 +
 +        IDateServiceListener
 +
 +
 +
 +</​Code>​
 +
 +
 +==== 2. Create an aspect that lifts the interface implementation declaration to the aspect ====
 +
 +=== create the aspect ===
 +<Code lang-java>​
 +public aspect DateServiceUserAspect {
 +    declare parents: (@DateServiceUser *) extends IDateServiceListener;​
 +}
 +
 +</​Code>​
 +
 +
 +=== remove the direct implementation declaration from the views (example) ===
 +<Code lang-java>​
 +
 +
 +
 +@DateServiceUser
 +
 +public class TimeEvaluationView extends ViewPart ​
 +
 +
 +
 +</​Code>​
 +
 +==== 3. Lifting the source check up to the aspect by introducing another interface in the aspect ====
 +
 +
 +[[{java2htmlplugin
 +
 +
 +
 +publicaspectdateserviceuseraspect{
 +
 +
 +
 +protectedinterfaceidateservicelisteneraspectinterfaceextendsidateservicelistener{
 +
 +
 +
 +voiddatechange(datenewdate);​
 +
 +
 +
 +}
 +
 +
 +
 +declareparents>​(@dateserviceuser*)extendsidateservicelisteneraspectinterface;​
 +
 +
 +
 +publicvoididateservicelisteneraspectinterface.datechange(datenewdate,​objectsource){
 +
 +if(this==source){
 +
 +return;
 +
 +}
 +
 +
 +
 +datechange(newdate);​
 +
 +}
 +
 +
 +
 +}
 +
 +
 +
 +}|{Java2HtmlPlugin
 +
 +
 +
 +public aspect DateServiceUserAspect {
 +
 +
 +
 +    protected interface IDateServiceListenerAspectInterface extends IDateServiceListener {
 +
 +        ​
 +
 +        void dateChange(Date newDate);
 +
 +        ​
 +
 +    }
 +
 +    ​
 +
 +    declare parents: (@DateServiceUser *) extends IDateServiceListenerAspectInterface;​
 +
 +    ​
 +
 +    public void IDateServiceListenerAspectInterface.dateChange(Date newDate, Object source) {
 +
 +        if (this == source) {
 +
 +            return;
 +
 +        }
 +
 +        ​
 +
 +        dateChange(newDate);​
 +
 +    }
 +
 +    ​
 +
 +}
 +
 +
 +
 +}]]
 +
 +
 +
 +The DateServiceUsers have to be changed accordingly. Now, the parameter source is only used in the aspect issues (it was unnessesary for the update of the date). Additionally,​ it is used indirectly in all @DateServiceUser classes, so code quality has improved.
 +
 +
 +==== 4. Minor changes ====
 +
 +=== moving assertions into the aspect ===
 +
 +=== inlining some not-so-relavant-methods ===
 +
 +==== 5. Moving the current date and some checks to the aspect ====
 +
 +=== aspect ===
 +
 +
 +[[{java2htmlplugin
 +
 +
 +
 +publicaspectdateserviceuseraspect{
 +
 +
 +
 +protectedinterfaceidateservicelisteneraspectinterfaceextends
 +
 +idateservicelistener{
 +
 +
 +
 +voiddatechange(datenewdate);​
 +
 +
 +
 +dategetcurrentdate();​
 +
 +
 +
 +booleanisdatechanged(datenewdate);​
 +
 +
 +
 +}
 +
 +
 +
 +declareparents>​(@dateserviceuser*)extendsidateservicelisteneraspectinterface;​
 +
 +
 +
 +publicvoididateservicelisteneraspectinterface.datechange(datenewdate,​
 +
 +objectsource){
 +
 +
 +
 +assertnewdate!=null;​
 +
 +
 +
 +if(this==source){
 +
 +return;
 +
 +}
 +
 +
 +
 +if(currentdate!=null&&​!isdatechanged(newdate)){
 +
 +return;
 +
 +}
 +
 +
 +
 +currentdate=newdate;​
 +
 +
 +
 +datechange(newdate);​
 +
 +}
 +
 +
 +
 +privatedateidateservicelisteneraspectinterface.currentdate;​
 +
 +
 +
 +publicdateidateservicelisteneraspectinterface.getcurrentdate(){
 +
 +returncurrentdate;​
 +
 +}
 +
 +
 +
 +..defaultimplementation
 +
 +publicbooleanidateservicelisteneraspectinterface.isdatechanged(
 +
 +datenewdate){
 +
 +returntrue;
 +
 +}
 +
 +
 +
 +}
 +
 +
 +
 +}|{Java2HtmlPlugin
 +
 +
 +
 +public aspect DateServiceUserAspect {
 +
 +
 +
 +    protected interface IDateServiceListenerAspectInterface extends
 +
 +            IDateServiceListener {
 +
 +
 +
 +        void dateChange(Date newDate);
 +
 +
 +
 +        Date getCurrentDate();​
 +
 +
 +
 +        boolean isDateChanged(Date newDate);
 +
 +
 +
 +    }
 +
 +
 +
 +    declare parents: (@DateServiceUser *) extends IDateServiceListenerAspectInterface;​
 +
 +
 +
 +    public void IDateServiceListenerAspectInterface.dateChange(Date newDate,
 +
 +            Object source) {
 +
 +
 +
 +        assert newDate != null;
 +
 +
 +
 +        if (this == source) {
 +
 +            return;
 +
 +        }
 +
 +
 +
 +        if (currentDate != null && !isDateChanged(newDate)) {
 +
 +            return;
 +
 +        }
 +
 +
 +
 +        currentDate = newDate;
 +
 +
 +
 +        dateChange(newDate);​
 +
 +    }
 +
 +
 +
 +    private Date IDateServiceListenerAspectInterface.currentDate;​
 +
 +
 +
 +    public Date IDateServiceListenerAspectInterface.getCurrentDate() {
 +
 +        return currentDate;​
 +
 +    }
 +
 +
 +
 +    // default implementation
 +
 +    public boolean IDateServiceListenerAspectInterface.isDateChanged(
 +
 +            Date newDate) {
 +
 +        return true;
 +
 +    }
 +
 +
 +
 +}
 +
 +
 +
 +}]]
 +
 +
 +=== changes in the classes (example: TimeEvaluationView) ===
 +
 +
 +<Code lang-java>​
 +
 +
 +
 +    public boolean isDateChanged(Date newDate) {
 +
 +        return !new SameWeekFilter(getCurrentDate()).evaluate(newDate);​
 +
 +    }
 +
 +
 +
 +    public void dateChange(Date newDate) {
 +
 +        if (!linkAction.isChecked()) {
 +
 +            return;
 +
 +        }
 +
 +
 +
 +        refresh();
 +
 +    }
 +
 +
 +
 +</​Code>​
 +
 +
 +
 +At this state, the code is somewhat broken because the check of the link action is executed after changing the current date. Step 6 solves this.
 +
 +
 +==== 6. extracting the link action to the aspect ====
 +
 +=== Adding another annotation for date services users with link actions ===
 +
 +
 +<Code lang-java>​
 +
 +
 +
 +@Retention(RetentionPolicy.RUNTIME)
 +
 +@Target( { ElementType.TYPE })
 +
 +@Documented
 +
 +public @interface DateServiceUserWithLinkAction {
 +
 +    ​
 +
 +}
 +
 +
 +
 +</​Code>​
 +
 +
 +=== Adding an additional interface to the aspect and extend the marked classes with that interface ===
 +
 +
 +[[{java2htmlplugin
 +
 +
 +
 +protectedinterfaceidateservicelistenerwithlinkactionaspectinterface
 +
 +extendsidateservicelisteneraspectinterface{
 +
 +}
 +
 +
 +
 +declareparents>​(@dateserviceuserwithlinkaction*)extendsidateservicelistenerwithlinkactionaspectinterface;​
 +
 +
 +
 +}|{Java2HtmlPlugin
 +
 +
 +
 +    protected interface IDateServiceListenerWithLinkActionAspectInterface
 +
 +            extends IDateServiceListenerAspectInterface {
 +
 +    }
 +
 +
 +
 +    declare parents: (@DateServiceUserWithLinkAction *) extends IDateServiceListenerWithLinkActionAspectInterface;​
 +
 +
 +
 +}]]
 +
 +
 +=== Change the classes that have a link action and use the date service accordingly (example) ===
 +
 +
 +<Code lang-java>​
 +
 +
 +
 +@DateServiceUserWithLinkAction
 +
 +public class ScheduleView extends ViewPart
 +
 +
 +
 +</​Code>​
 +
 +
 +=== Allow access to the link action using that interface ===
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +<Code lang-java>​
 +
 +
 +
 +    protected interface IDateServiceListenerWithLinkActionAspectInterface
 +
 +            extends IDateServiceListenerAspectInterface {
 +
 +        ​
 +
 +        LinkAction getLinkAction();​
 +
 +        ​
 +
 +    }
 +
 +
 +
 +</​Code>​
 +
 +
 +
 +Implementation in ScheduleView (Example):
 +
 +
 +
 +<Code lang-java>​
 +
 +
 +
 +    public LinkAction getLinkAction() {
 +
 +        return linkAction;
 +
 +    }
 +
 +  ​
 +
 +</​Code>​
 +
 +
 +=== Add an arround advice to the aspect that takes care of the link action if available ===
 +
 +
 +[[{java2htmlplugin
 +
 +
 +
 +voidaround(datenewdate,​objectsource,​
 +
 +idateservicelisteneraspectinterfacetargetobject)>​
 +
 +execution(voididateservicelisteneraspectinterface.datechange(date,​
 +
 +object))&&​args(newdate,​source)&&​target(targetobject){
 +
 +
 +
 +if(targetobjectinstanceofidateservicelistenerwithlinkactionaspectinterface){
 +
 +linkactionlinkaction=((idateservicelistenerwithlinkactionaspectinterface)targetobject)
 +
 +.getlinkaction();​
 +
 +
 +
 +if(!linkaction.ischecked()){
 +
 +return;
 +
 +}
 +
 +}
 +
 +
 +
 +proceed(newdate,​source,​targetobject);​
 +
 +}
 +
 +
 +
 +}|{Java2HtmlPlugin
 +
 +
 +
 +    void around(Date newDate, Object source,
 +
 +            IDateServiceListenerAspectInterface targetObject): ​
 +
 +        execution(void IDateServiceListenerAspectInterface.dateChange(Date,​
 +
 +                Object)) && args(newDate,​source) && target(targetObject) {
 +
 +
 +
 +        if (targetObject instanceof IDateServiceListenerWithLinkActionAspectInterface) {
 +
 +            LinkAction linkAction = ((IDateServiceListenerWithLinkActionAspectInterface) targetObject)
 +
 +                    .getLinkAction();​
 +
 +
 +
 +            if (!linkAction.isChecked()) {
 +
 +                return;
 +
 +            }
 +
 +        }
 +
 +
 +
 +        proceed(newDate,​ source, targetObject);​
 +
 +    }
 +
 +   
 +
 +}]]
 +
 +
 +=== Remove the link action state checking from the classes (example: TimeEvaluationView) ===
 +
 +
 +<Code lang-java>​
 +
 +
 +
 +    public void dateChange(Date newDate) {
 +
 +        refresh();
 +
 +    }
 +
 +   
 +
 +</​Code>​
 +
 +
 +===== State after the refactoring =====
 +
 +==== Annotations ====
 +
 +
 +<Code lang-java>​
 +
 +
 +
 +@Retention(RetentionPolicy.RUNTIME)
 +
 +@Target( { ElementType.TYPE })
 +
 +@Documented
 +
 +public @interface DateServiceUser {
 +
 +    ​
 +
 +}
 +
 +   
 +
 +</​Code>​
 +
 +
 +
 +<Code lang-java>​
 +
 +
 +
 +@Retention(RetentionPolicy.RUNTIME)
 +
 +@Target( { ElementType.TYPE })
 +
 +@Documented
 +
 +public @interface DateServiceUserWithLinkAction {
 +
 +    ​
 +
 +}
 +
 +   
 +
 +</​Code>​
 +
 +
 +==== Aspect ====
 +
 +
 +[[{java2htmlplugin
 +
 +
 +
 +publicaspectdateserviceuseraspect{
 +
 +
 +
 +protectedinterfaceidateservicelisteneraspectinterfaceextends
 +
 +idateservicelistener{
 +
 +
 +
 +voiddatechange(datenewdate);​
 +
 +
 +
 +dategetcurrentdate();​
 +
 +
 +
 +booleanisdatechanged(datenewdate);​
 +
 +
 +
 +}
 +
 +
 +
 +declareparents>​(@dateserviceuser*)extendsidateservicelisteneraspectinterface;​
 +
 +
 +
 +publicvoididateservicelisteneraspectinterface.datechange(datenewdate,​
 +
 +objectsource){
 +
 +
 +
 +assertnewdate!=null;​
 +
 +
 +
 +if(this==source){
 +
 +return;
 +
 +}
 +
 +
 +
 +if(currentdate!=null&&​!isdatechanged(newdate)){
 +
 +return;
 +
 +}
 +
 +
 +
 +currentdate=newdate;​
 +
 +
 +
 +datechange(newdate);​
 +
 +}
 +
 +
 +
 +privatedateidateservicelisteneraspectinterface.currentdate;​
 +
 +
 +
 +publicdateidateservicelisteneraspectinterface.getcurrentdate(){
 +
 +returncurrentdate;​
 +
 +}
 +
 +
 +
 +..defaultimplementation
 +
 +publicbooleanidateservicelisteneraspectinterface.isdatechanged(
 +
 +datenewdate){
 +
 +returntrue;
 +
 +}
 +
 +
 +
 +protectedinterfaceidateservicelistenerwithlinkactionaspectinterface
 +
 +extendsidateservicelisteneraspectinterface{
 +
 +
 +
 +linkactiongetlinkaction();​
 +
 +
 +
 +}
 +
 +
 +
 +declareparents:​(@dateserviceuserwithlinkaction*)extendsidateservicelistenerwithlinkactionaspectinterface;​
 +
 +
 +
 +voidaround(datenewdate,​objectsource,​
 +
 +idateservicelisteneraspectinterfacetargetobject):​
 +
 +execution(voididateservicelisteneraspectinterface.datechange(date,​
 +
 +object))&&​args(newdate,​source)&&​target(targetobject){
 +
 +
 +
 +if(targetobjectinstanceofidateservicelistenerwithlinkactionaspectinterface){
 +
 +linkactionlinkaction=((idateservicelistenerwithlinkactionaspectinterface)targetobject)
 +
 +.getlinkaction();​
 +
 +
 +
 +if(!linkaction.ischecked()){
 +
 +return;
 +
 +}
 +
 +}
 +
 +
 +
 +proceed(newdate,​source,​targetobject);​
 +
 +}
 +
 +}
 +
 +
 +
 +}|{Java2HtmlPlugin
 +
 +
 +
 +public aspect DateServiceUserAspect {
 +
 +
 +
 +    protected interface IDateServiceListenerAspectInterface extends
 +
 +            IDateServiceListener {
 +
 +
 +
 +        void dateChange(Date newDate);
 +
 +
 +
 +        Date getCurrentDate();​
 +
 +
 +
 +        boolean isDateChanged(Date newDate);
 +
 +
 +
 +    }
 +
 +
 +
 +    declare parents: (@DateServiceUser *) extends IDateServiceListenerAspectInterface;​
 +
 +
 +
 +    public void IDateServiceListenerAspectInterface.dateChange(Date newDate,
 +
 +            Object source) {
 +
 +
 +
 +        assert newDate != null;
 +
 +
 +
 +        if (this == source) {
 +
 +            return;
 +
 +        }
 +
 +
 +
 +        if (currentDate != null && !isDateChanged(newDate)) {
 +
 +            return;
 +
 +        }
 +
 +
 +
 +        currentDate = newDate;
 +
 +
 +
 +        dateChange(newDate);​
 +
 +    }
 +
 +
 +
 +    private Date IDateServiceListenerAspectInterface.currentDate;​
 +
 +
 +
 +    public Date IDateServiceListenerAspectInterface.getCurrentDate() {
 +
 +        return currentDate;​
 +
 +    }
 +
 +
 +
 +    // default implementation
 +
 +    public boolean IDateServiceListenerAspectInterface.isDateChanged(
 +
 +            Date newDate) {
 +
 +        return true;
 +
 +    }
 +
 +
 +
 +    protected interface IDateServiceListenerWithLinkActionAspectInterface
 +
 +            extends IDateServiceListenerAspectInterface {
 +
 +
 +
 +        LinkAction getLinkAction();​
 +
 +
 +
 +    }
 +
 +
 +
 +    declare parents: (@DateServiceUserWithLinkAction *) extends IDateServiceListenerWithLinkActionAspectInterface;​
 +
 +
 +
 +    void around(Date newDate, Object source,
 +
 +            IDateServiceListenerAspectInterface targetObject): ​
 +
 +        execution(void IDateServiceListenerAspectInterface.dateChange(Date,​
 +
 +                Object)) && args(newDate,​source) && target(targetObject) {
 +
 +
 +
 +        if (targetObject instanceof IDateServiceListenerWithLinkActionAspectInterface) {
 +
 +            LinkAction linkAction = ((IDateServiceListenerWithLinkActionAspectInterface) targetObject)
 +
 +                    .getLinkAction();​
 +
 +
 +
 +            if (!linkAction.isChecked()) {
 +
 +                return;
 +
 +            }
 +
 +        }
 +
 +
 +
 +        proceed(newDate,​ source, targetObject);​
 +
 +    }
 +
 +}
 +
 +   
 +
 +}]]
 +
 +
 +==== Classes (Views) ====
 +
 +=== Relevant code from TimeEvaluationView ===
 +
 +
 +<Code lang-java>​
 +
 +
 +
 +@DateServiceUserWithLinkAction
 +
 +public class TimeEvaluationView extends ViewPart {
 +
 +
 +
 +    private LinkAction linkAction;
 +
 +
 +
 +    public LinkAction getLinkAction() {
 +
 +        return linkAction;
 +
 +    }
 +
 +   
 +
 +    public boolean isDateChanged(Date newDate) {
 +
 +        return !new SameWeekFilter(getCurrentDate()).evaluate(newDate);​
 +
 +    }
 +
 +
 +
 +    public void dateChange(Date newDate) {
 +
 +        refresh();
 +
 +    }
 +
 +
 +
 +...
 +
 +
 +
 +}
 +
 +   
 +
 +</​Code>​
 +
 +
 +=== Relevant code from ScheduleView ===
 +
 +
 +<Code lang-java>​
 +
 +
 +
 +@DateServiceUserWithLinkAction
 +
 +public class ScheduleView extends ViewPart {
 +
 +
 +
 +    private LinkAction linkAction;
 +
 +
 +
 +    public LinkAction getLinkAction() {
 +
 +        return linkAction;
 +
 +    }
 +
 +
 +
 +    public void dateChange(Date newDate) {
 +
 +        getAction(SCHEDULE_VIEW_FILTER_DAY).changeFilter(
 +
 +                new DateFilterToInformationFilterAdapter(new SameDayFilter(
 +
 +                        newDate)));
 +
 +        getAction(SCHEDULE_VIEW_FILTER_WEEK).changeFilter(
 +
 +                new DateFilterToInformationFilterAdapter(new SameWeekFilter(
 +
 +                        newDate)));
 +
 +        getAction(SCHEDULE_VIEW_FILTER_MONTH).changeFilter(
 +
 +                new DateFilterToInformationFilterAdapter(new SameMonthFilter(
 +
 +                        newDate)));
 +
 +    }
 +
 +
 +
 +...
 +
 +
 +
 +}
 +
 +   
 +
 +</​Code>​
 +
 +
 +
 +
 +
 +
 +=== Relevant code from DayView ===
 +
 +
 +<Code lang-java>​
 +
 +
 +
 +@DateServiceUser
 +
 +public class DayView extends ViewPart {
 +
 +
 +
 +    public void dateChange(Date newDate) {
 +
 +        setModel(DiaryUtil.getDay(newDate));​
 +
 +    }
 +
 +
 +
 +...
 +
 +
 +
 +}
 +
 +   
 +
 +</​Code>​
 +
 +
 +=== Relevant code from WeekOverviewView ===
 +
 +
 +<Code lang-java>​
 +
 +
 +
 +@DateServiceUserWithLinkAction
 +
 +public class WeekOverviewView extends ViewPart {
 +
 +
 +
 +    private LinkAction linkAction;
 +
 +
 +
 +    public LinkAction getLinkAction() {
 +
 +        return linkAction;
 +
 +    }
 +
 +   
 +
 +    public boolean isDateChanged(Date newDate) {
 +
 +        return !new SameWeekFilter(getCurrentDate()).evaluate(newDate);​
 +
 +    }
 +
 +
 +
 +    public void dateChange(Date newDate) {
 +
 +        viewer.setInput(dateConverter.convert(newDate));​
 +
 +    }
 +
 +
 +
 +...
 +
 +
 +
 +}
 +
 +
 +
 +</​Code>​
 +
 +
 +=== Relevant code from CalendarView ===
 +
 +
 +<Code lang-java>​
 +
 +
 +
 +@DateServiceUser
 +
 +public class CalendarView extends ViewPart {
 +
 +
 +
 +    public void dateChange(Date newDate) {
 +
 +        Calendar calendar = Calendar.getInstance();​
 +
 +        calendar.setTime(newDate);​
 +
 +        swtCalendar.setCalendar(calendar);​
 +
 +    }
 +
 +
 +
 +...
 +
 +
 +
 +}
 +
 +
 +
 +</​Code>​
 +
 +
 +
 +
 +
 +
 +=== Relevant code from PastEventsView ===
 +
 +
 +<Code lang-java>​
 +
 +
 +
 +@DateServiceUser
 +
 +public class PastEventsView extends ViewPart {
 +
 +
 +
 +    public void dateChange(Date newDate) {
 +
 +        update();
 +
 +    }
 +
 +
 +
 +...
 +
 +
 +
 +}
 +
 +
 +
 +</​Code>​
 +
 +
 +===== Evaluation of the result =====
 +
 +=== consistency ===
 +
 +
 +A lot of consistency was gained by moving the checks into the aspect. For example, now, it is always checked whether the source is the same as the current object before change the date. Additionally,​ the complexity in the view classes was reduced because the only see what they need to (for example not the source of the date change).
 +
 +
 +=== reusability ===
 +
 +
 +The aspect and the annotion classes are reusable. The effort to implement new views which change their values based on the selected date is expected to be reduced a lot, and the quality of these implementations is expected to be higher, because the checks are contained in the aspect that is reused.
 +
 +
 +=== complexity ===
 +
 +
 +The complexity of using the date selection is reduced, because it comes down to adding an annotation and implementing some methods required by the aspect interfaces.
 +
 +
 +
 +But the complexity of changing the aspect is higher with the new solution. This is because the aspect has to handle all cases of classes, not just one as before. The advantage is that once the aspect is changed, the changes apply to all @DateServiceUser marked classes.
 +
 +
 +=== comprehensibility ===
 +
 +
 +The new solution is harder to understand, at least at first. I expect that is will be easier once one is used to such a pattern, because the amount of directly visible information is reduced in the  @DateServiceUser marked classes.
 +
 +
 +===== Further Work =====
 +
 +==== Annotation attributation and inheritance ====
 +
 +The solution with two different annotations is not very elegant. Using annotation attributes or annotation inheritance (is that possible?) would be better.
 +==== Link action extraction ====
 +
 +The link action could be moved more or less completly to the aspect, which would be a lot better. ​
 +==== Compare to multiple inheritance ====
 +
 +The result smells like using aspects and annotations to gain some sort of multiple inheritance to me. Multiple inheritance was not included in Java for good? reasons. So it would be interesting what kind of inheritance is used here (--> B.Meyer) and how it can be compared to multiple inheritance.
 +===== Remarks =====
 +
 +==== Weaving aspects from required plugins in eclipse using multiple projects ====
 +
 +Use "​(Project) Properties >> AspectJ Aspect Path >> Add class folder"​ to add the class folder containing the aspects to be woven.
research/cultivate/refactoringtoaspectsexamplenomadpimidateservicelistener.txt · Last modified: 2018/05/09 01:59 (external edit)

SEWiki, © 2019