没有开关和条件语句的角落逻辑的设计模式

时间:2015-06-09 00:00:57

标签: java notifications

我有一个包含许多switch语句的类。代码对我来说看起来很难看,但我无法弄清楚如何修复它。如果有人可以提出一个设计模式或一个清理它的技巧,这将是伟大的。下面是Class的缩写版本,带有一些上下文注释。

/**
 * Slides a desktop Notification from the edge of the screen into the desktop by some margin. 
 */
public class SlideManager extends NotificationManager {
    // Location enum provided below for reference:
    /*public enum Location {
        NORTH, NORTHEAST, EAST, SOUTHEAST, SOUTH, SOUTHWEST, WEST, NORTHWEST
    }*/
    private Location m_loc; // the corner / edge of the screen where the Notification should appear
    // A Screen abstracts away the padding logic.
    // The idea was that you would just give it a padding and I could reuse
    // it later in all my NotificationManagers to give me the x and y for a Notification Location later.
    private Screen m_standardScreen;
    private Screen m_noPaddingScreen;
    // the direction that the Notification should slide in from.
    // For instance, if we had our Notification show up on the center east side of the screen,
    // it should slide in from the edge towards the west until it was a suitable margin away from the edge of the screen.
    private SlideDirection m_direction;
    private double m_slideSpeed;
    // this is a flag to signal when the user has overridden the default SlideDirection calculation.
    // If the user constructs the SlideManager without calling setSlideDirection later,
    // this will be false. However, if the user does setSlideDirection
    // we want to avoid changing that automatically if the user later calls setLocation.
    private boolean m_overwrite;

    public enum SlideDirection {
        NORTH, SOUTH, EAST, WEST
    }

    {
        m_standardScreen = Screen.standard();
        m_noPaddingScreen = Screen.withPadding(0);
        m_slideSpeed = 300;
        m_overwrite = false;
    }

    public SlideManager() {
        m_loc = Location.NORTHEAST;
        recalculateSlideDirection();
    }

    public SlideManager(Location loc) {
        m_loc = loc;
        recalculateSlideDirection();
    }

    /**
     * Sets the location where the Notifications show up.
     * If the user has not explicitly given a SlideDirection, this will also recalculate the SlideDirection. 
     * For example, if the user moves the Notification spawn location from East to West 
     * we want to change the SlideDirection from West to East, respectively.
     *
     * @param loc
     */
    public void setLocation(Location loc) {
        m_loc = loc;
        if (!m_overwrite)
            recalculateSlideDirection();
    }

    /**
     * Sets the direction that the Notification should slide in from.
     *
     * @param slide
     */
    public void setSlideDirection(SlideDirection slide) {
        m_direction = slide;
        m_overwrite = true;
    }

    private void recalculateSlideDirection() {
        switch (m_loc) {
        // The tricky part is when the user wants the Notification to appear in a corner of the screen.
        // If this weren't the case, it would make no sense for a user to set
        // his own SlideDirection because the SlideDirection for Location.
        // EAST would always be West, Location.NORTH would always be SOUTH, etc.
        // But for the (literal) corner cases I want to somehow give a way for the user to have a preference.
        // For example, in the top right corner the Notification could either slide in from the right or from the top.
        // By default I choose the top, but I want the user to be able to change this and also have that choice remembered if the Location changes.
        case NORTHWEST:
            m_direction = SlideDirection.SOUTH;
            break;
        case NORTH:
            m_direction = SlideDirection.SOUTH;
            break;
        case NORTHEAST:
            m_direction = SlideDirection.SOUTH;
            break;
        case EAST:
            m_direction = SlideDirection.WEST;
            break;
        case SOUTHEAST:
            m_direction = SlideDirection.NORTH;
            break;
        case SOUTH:
            m_direction = SlideDirection.NORTH;
            break;
        case SOUTHWEST:
            m_direction = SlideDirection.NORTH;
            break;
        case WEST:
            m_direction = SlideDirection.EAST;
            break;
        }
    }

    /*
        When a Notification is added it should slide in from the edge towards an area slightly off the edge.
    */
    @Override
    protected void notificationAdded(Notification note, Time time) {
        int noPaddingX = m_noPaddingScreen.getX(m_loc, note);
        int noPaddingY = m_noPaddingScreen.getY(m_loc, note);
        int standardX = m_standardScreen.getX(m_loc, note);
        int standardY = m_standardScreen.getY(m_loc, note);

        Slider slider = null;
        double frequency = 50;
        double slideDelta = m_slideSpeed / frequency;

        // How would I abstract this?
        switch (m_direction) {
        case NORTH: {
            note.setLocation(standardX, noPaddingY);
            slider = new Slider(note, m_direction, 0, -slideDelta, standardX, standardY);
        }
            break;
        case SOUTH: {
            note.setLocation(standardX, noPaddingY);
            slider = new Slider(note, m_direction, 0, slideDelta, standardX, standardY);
        }
            break;
        case EAST: {
            note.setLocation(noPaddingX, standardY);
            slider = new Slider(note, m_direction, slideDelta, 0, standardX, standardY);
        }
            break;
        case WEST:
            note.setLocation(noPaddingX, standardY);
            slider = new Slider(note, m_direction, -slideDelta, 0, standardX, standardY);
            break;
        }

        Timer timer = new Timer((int) frequency, slider);
        timer.start();
        note.show();
    }

    /*Slides a Notification from its current location to a desired location with fixed deltaX's and deltaY's. It needs the SlideDirection to know which end values to check for stopping.*/
    public class Slider implements ActionListener {
        private Notification m_note;
        private SlideDirection m_dir;
        private double m_deltaX;
        private double m_deltaY;
        private double m_stopX;
        private double m_stopY;

        private double m_x;
        private double m_y;

        public Slider(Notification note, SlideDirection dir, double deltaX, double deltaY, double stopX, double stopY) {
            m_note = note;
            m_dir = dir;
            m_deltaX = deltaX;
            m_deltaY = deltaY;
            m_stopX = stopX;
            m_stopY = stopY;

            m_x = note.getX();
            m_y = note.getY();
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            m_x += m_deltaX;
            m_y += m_deltaY;

            // another ugly heap of conditionals. Is there a way to abstract away the stopping logic?
            if (m_dir == SlideDirection.SOUTH) {
                if (m_y >= m_stopY) {
                    m_y = m_stopY;
                    ((Timer) e.getSource()).stop();
                }
            } else if (m_dir == SlideDirection.NORTH) {
                if (m_y <= m_stopY) {
                    m_y = m_stopY;
                    ((Timer) e.getSource()).stop();
                }
            } else if (m_dir == SlideDirection.EAST) {
                if (m_x >= m_stopX) {
                    m_x = m_stopX;
                    ((Timer) e.getSource()).stop();
                }
            } else if (m_dir == SlideDirection.WEST) {
                if (m_x <= m_stopX) {
                    m_x = m_stopX;
                    ((Timer) e.getSource()).stop();
                }
            }

            m_note.setLocation((int) (m_x), (int) (m_y));
        }
    }
}

显然,拥有两个switch和一个条件链意味着出了问题。我最初的想法是制作某种类型的SlideVector类,我可以给它一个SlideDirection,它会计算翻译并处理最终案例。这种方法的优点/缺点是什么?在这种情况下,HashMap是否有用?还有其他方法可以解决这个问题吗?

1 个答案:

答案 0 :(得分:2)

查看策略模式。您可以将所有switch语句移动到不同的类中。你的类将有一个这个翻译类的实例,它有一个方法可以为你提供状态(所有信息),它会为你创建输出。

在recalculateSlideDirection中你可以使用fall through来保存在线上(虽然呃,它不会为你节省很多)。

notificationAdded的另一个选项是定义一个接口并创建一个hashmap,它将m_direction映射到该接口的实现。所以你基本上只有一个HashMap&lt; SlideDirection,IYourInterface&gt; yourMap ... 所以你基本上会调用yourMap.get(n_direction).action();这将取代你的switch语句。