java - 没有 switch 和条件语句的极端情况逻辑的设计模式

标签 java notifications

我有一个类,其中有许多 switch 语句。该代码对我来说看起来很丑陋,但我不知道如何修复它。如果有人可以提出一种设计模式或解决这个问题的技巧,那就太好了。下面是该类的缩写版本,其中包含一些上下文注释。

/**
 * 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 在这种情况下有什么用吗?还有其他方法可以解决这个问题吗?

最佳答案

看看策略模式。您可以将所有 switch 语句移至不同的类中。您的类将具有此翻译类的一个实例,该实例将具有一个方法,您可以向该方法提供状态(所有信息),并且它将为您创建输出。

在recalculateSlideDirection中,您可以使用fall through来节省行数(尽管呃,它不会为您节省很多)。

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

关于java - 没有 switch 和条件语句的极端情况逻辑的设计模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30720948/

相关文章:

java - java.lang.NoClassDefFoundError : Could not initialize class java test step definition class

android - NotificationCompat.Builder 中通知时出现 NullPointerException

c# 推送通知不适用于土耳其语字符

javascript - 在 Firefox 中连续使用 Notifications API 的多个通知

android - 在 RecyclerAdapter 中单击按钮时增加徽章值

Java 通过添加循环并删除对 display() 的调用来修改 run()!

java - 随机 HSV 颜色

java - NotificationCompat.BigPictureStyle 可以获取图片显示

java - 在 Java 中将具有不同布局的文件导入数据库

java - ... 不是访问器方法