3
\$\begingroup\$

Could someone review my code, with respect to OOP, clean code and MVC pattern?

Main app:

public class Application
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            //test
            @Override
            public void run()
            {
                runApplication();
            }
        });

    }

    public static void runApplication()
    {
        Model model = new Model();

        MainFrame view = new MainFrame(model);
        Controller controller = new Controller(view, model);

    }
}

Controller:

public class Controller
{
    final static Logger logger = Logger.getLogger(ToolsMenu.class);
    // private Color selectedColor = StaticStuff.getShapecolor();
    private MainFrame view;
    private Model model;

    private Shape drawShape;

    public Controller(MainFrame view, Model model)
    {
        this.view = view;
        this.model = model;

        this.view.getToolsMenu().addRectangleListener(new RectangleListener());
        this.view.getToolsMenu().addCircleListener(new CircleListener());
        this.view.getToolsMenu().addLineListener(new LineListener());
        this.view.getToolsMenu().addPencilListener(new PencilListener());
        this.view.getToolsMenu().addRubberListener(new RubberListener());
        this.view.getToolsMenu().addColorListener(new ColorListener());
        this.view.getToolsMenu().addFillClosedShapeListener(new FillClosedShapeListener());
        this.view.getDrawPanel().addDrawPanelMouseListener(new MouseListenerForDrawPanel());
        this.view.getDrawPanel().addDrawPanelMouseMotionListener(new MouseMotionListenerForDrawPanel());
    }

    class MouseMotionListenerForDrawPanel implements MouseMotionListener
    {

        @Override
        public void mouseDragged(MouseEvent event)
        {
            if (!(drawShape == null))
            {
                if (drawShape instanceof Pencil)
                {
                    ((Pencil) drawShape).addPoint(new Point(event.getX(), event.getY()));
                }
                else if (drawShape instanceof Rubber)
                {
                    ((Rubber) drawShape).addPoint(new Point(event.getX(), event.getY()));
                }
                else
                {
                    drawShape.setX2(event.getX());
                    drawShape.setY2(event.getY());
                }
                view.getDrawPanel().setTmpShape(drawShape);
                view.getDrawPanel().repaint();
            }

        }

        @Override
        public void mouseMoved(MouseEvent arg0)
        {
        }

    }

    class MouseListenerForDrawPanel implements MouseListener
    {
        public void mouseClicked(MouseEvent e)
        {

        }

        public void mouseEntered(MouseEvent e)
        {

        }

        public void mouseExited(MouseEvent e)
        {

        }

        public void mousePressed(MouseEvent e)
        {
            drawShape = getTmpShape(e.getX(), e.getY(), 2, 2);
            view.getDrawPanel().setTmpShape(drawShape);
        }

        public void mouseReleased(MouseEvent e)
        {
            if (!(drawShape == null))
            {
                drawShape.setX2(e.getX());
                drawShape.setY2(e.getY());

                List<Shape> shapes = view.getDrawPanel().getShapes();
                shapes.add(drawShape);
                view.getDrawPanel().setTmpShape(null);
                view.getDrawPanel().setShapes(shapes);
                drawShape = null;
                view.getDrawPanel().repaint();

            }
        }
    }

    class ColorListener implements ActionListener
    {

        @Override
        public void actionPerformed(ActionEvent event)
        {
            Color selectedColor = JColorChooser.showDialog(view, "Chose color:", StaticStuff.getShapeColor());
            StaticStuff.setShapeColor(selectedColor);
            view.getToolsMenu().setColor(selectedColor);
        }
    }

    class RectangleListener implements ActionListener
    {

        @Override
        public void actionPerformed(ActionEvent event)
        {
            StaticStuff.setShapeType(ShapeEnum.RECTANGLE);
        }
    }

    class CircleListener implements ActionListener
    {

        @Override
        public void actionPerformed(ActionEvent e)
        {
            StaticStuff.setShapeType(ShapeEnum.CIRCLE);
        }
    }

    class LineListener implements ActionListener
    {

        @Override
        public void actionPerformed(ActionEvent e)
        {
            StaticStuff.setShapeType(ShapeEnum.LINE);
        }
    }

    class PencilListener implements ActionListener
    {

        @Override
        public void actionPerformed(ActionEvent e)
        {
            StaticStuff.setShapeType(ShapeEnum.PENICL);
        }
    }

    class RubberListener implements ActionListener
    {

        @Override
        public void actionPerformed(ActionEvent e)
        {
            StaticStuff.setShapeType(ShapeEnum.RUBBER);
        }
    }

    class FillClosedShapeListener implements ActionListener
    {

        @Override
        public void actionPerformed(ActionEvent arg0)
        {
            StaticStuff.setShapeType(ShapeEnum.FILLCLOSEDSHAEP);
        }

    }

    private Shape getTmpShape(int x, int y, int x2, int y2)
    {
        switch (StaticStuff.getShapeType())
        {
        case RECTANGLE:
            return new Rectangle(x, y, x2, y2, StaticStuff.getShapeColor());
        case CIRCLE:
            return new Circle(x, y, x2, y2, StaticStuff.getShapeColor());
        case PENICL:
            return new Pencil(x, y, x2, y2, StaticStuff.getShapeColor());
        case LINE:
            return new Line(x, y, x2, y2, StaticStuff.getShapeColor());
        case RUBBER:
            return new Rubber(x, y, x2, y2, Color.WHITE);
        default:
            break;
        }
        return drawShape;
    }
}

Model:

public class StaticStuff
{
    private static ShapeEnum shapeType = ShapeEnum.RECTANGLE;
    private static Color shapeColor = Color.BLACK;

    public static ShapeEnum getShapeType()
    {
        return shapeType;
    }

    public static void setShapeType(ShapeEnum shapeType)
    {
        StaticStuff.shapeType = shapeType;
    }

    public static Color getShapeColor()
    {
        return shapeColor;
    }

    public static void setShapeColor(Color shapeColor)
    {
        StaticStuff.shapeColor = shapeColor;
    }
}

Super class for each shape:

    public abstract class Shape
    {
        private int x, y;
        private int x2, y2;
        private Color color;

        public Shape(int x, int y, int x2, int y2, Color c)
        {
            this.x = x;
            this.y = y;
            this.x2 = x2;
            this.y2 = y2;
            color = c;
        }

        public abstract void render(Graphics g);

        public int calcWidth()
        {
            return x2 - x;
        }

        public int calcHeight()
        {
            return y2 - y;
        }

        public int getX()
        {
            return x;
        }

        public void setX(int x)
        {
            this.x = x;
        }

        public int getY()
        {
            return y;
        }

        public void setY(int y)
        {
            this.y = y;
        }

        public int getX2()
        {
            return x2;
        }

        public void setX2(int x2)
        {
            this.x2 = x2;
        }

        public in

t getY2()
    {
        return y2;
    }

    public void setY2(int y2)
    {
        this.y2 = y2;
    }

    public Color getColor()
    {
        return color;
    }

    public void setColor(Color color)
    {
        this.color = color;
    }

}

View part:

public class MainFrame extends JFrame
{

    private TopMenu menuBar;
    private DrawPanel drawPanel;
    private ToolsMenu toolsMenu;

    public MainFrame(Model model) throws HeadlessException
    {
        super("Paint Application");
        prepareFrame();

        initializeToolsMenuComponents();
        initializeTopMenuComponents();
        drawPanel = new DrawPanel();

        JPanel mainLayoutforApplication = new JPanel(new BorderLayout());

        mainLayoutforApplication.add(toolsMenu, BorderLayout.WEST);
        mainLayoutforApplication.add(drawPanel, BorderLayout.CENTER);
        this.add(mainLayoutforApplication);

    }

    public void prepareFrame()
    {
        setSize(500, 500);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        // setResizable(false);
        setVisible(true);
    }

    public void initializeTopMenuComponents()
    {
        menuBar = new TopMenu();
        menuBar.setTopMenu();
        setJMenuBar(menuBar.getMenuBar());
    }

    public void initializeToolsMenuComponents()
    {
        toolsMenu = new ToolsMenu();
        toolsMenu.setTools();
    }

    public ToolsMenu getToolsMenu()
    {
        return toolsMenu;
    }

    public DrawPanel getDrawPanel()
    {
        return drawPanel;
    }
}

DrawPanel:

public class DrawPanel extends JPanel
{
    private List<Shape> shapes = new ArrayList<>();
    private Shape demoShape;

    public DrawPanel()
    {
        setOpaque(true);
        setBackground(Color.WHITE);
    }

    public void addDrawPanelMouseListener(MouseListener listenForMouse)
    {
        this.addMouseListener(listenForMouse);
    }

    public void addDrawPanelMouseMotionListener(MouseMotionListener listenForMouse)
    {
        this.addMouseMotionListener(listenForMouse);
    }

    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);

        for (Shape s : shapes)
        {
            if (s != null)
                s.render(g);
        }

        if (demoShape != null)
            demoShape.render(g);
    }

    public List<Shape> getShapes()
    {
        return shapes;
    }

    public void setShapes(List<Shape> shapes)
    {
        this.shapes = shapes;
    }

    public Shape getTmpShape()
    {
        return demoShape;
    }

    public void setTmpShape(Shape tmpShape)
    {
        this.demoShape = tmpShape;
    }

}

ToolsMenu:

public class ToolsMenu extends JPanel
{

    private JButton colorButton, rectangleButton, circleButton, pencilButton, lineButton, rubberButton,
            fillClosedShapeButton;
    private JLabel tipText;

    public ToolsMenu()
    {
    }

    public void setTools()
    {
        int numberOfColumns = 1; // = 0 many columns as necessary
        int numberOfRows = 0; // 0 = many rows as necessary
        this.setLayout(new GridLayout(numberOfRows, numberOfColumns));

        tipText = new JLabel("Color Chooser: ");
        rectangleButton = new JButton(new ImageIcon("images/tools/rectangle.png"));
        circleButton = new JButton(new ImageIcon("images/tools/circle.png"));
        pencilButton = new JButton(new ImageIcon("images/tools/pencil.png"));
        lineButton = new JButton(new ImageIcon("images/tools/line.png"));
        rubberButton = new JButton(new ImageIcon("images/tools/rubber.png"));
        fillClosedShapeButton = new JButton(new ImageIcon("images/tools/fill.png"));

        colorButton = new JButton();
        colorButton.setForeground(StaticStuff.getShapeColor());
        colorButton.setBackground(StaticStuff.getShapeColor());

        this.add(circleButton);
        this.add(rectangleButton);
        this.add(pencilButton);
        this.add(lineButton);
        this.add(rubberButton);
        this.add(fillClosedShapeButton);
        this.add(tipText);
        this.add(colorButton);
    }

    public void addColorListener(ActionListener listenForColorButton)
    {
        colorButton.addActionListener(listenForColorButton);
    }

    public void addRectangleListener(ActionListener listenForRectangleButton)
    {
        rectangleButton.addActionListener(listenForRectangleButton);
    }

    public void addCircleListener(ActionListener listenForCircleButton)
    {
        circleButton.addActionListener(listenForCircleButton);
    }

    public void addLineListener(ActionListener listenForLineButton)
    {
        lineButton.addActionListener(listenForLineButton);
    }

    public void addPencilListener(ActionListener listenForPencilButton)
    {
        pencilButton.addActionListener(listenForPencilButton);
    }

    public void addRubberListener(ActionListener listenForRubberButton)
    {
        rubberButton.addActionListener(listenForRubberButton);
    }

    public void addFillClosedShapeListener(ActionListener listenForFillClosedShapeButton)
    {
        fillClosedShapeButton.addActionListener(listenForFillClosedShapeButton);
    }

    public void setColor(Color color)
    {
        colorButton.setForeground(color);
        colorButton.setBackground(color);
    }
}
\$\endgroup\$
0

1 Answer 1

1
\$\begingroup\$
    this.view.getToolsMenu().addRectangleListener(new RectangleListener());
    this.view.getToolsMenu().addCircleListener(new CircleListener());
    this.view.getToolsMenu().addLineListener(new LineListener());
    this.view.getToolsMenu().addPencilListener(new PencilListener());
    this.view.getToolsMenu().addRubberListener(new RubberListener());
    this.view.getToolsMenu().addColorListener(new ColorListener());
    this.view.getToolsMenu().addFillClosedShapeListener(new FillClosedShapeListener());

Use a temporary variable, this increases the readability.


            if (drawShape instanceof Pencil)
            {
                ((Pencil) drawShape).addPoint(new Point(event.getX(), event.getY()));
            }
            else if (drawShape instanceof Rubber)
            {
                ((Rubber) drawShape).addPoint(new Point(event.getX(), event.getY()));
            }

This seems like a place where you would have to add new functionality all the time. Is there any way you could implement a strategy pattern here?


    public void mouseClicked(MouseEvent e)
    {

    }

    public void mouseEntered(MouseEvent e)
    {

    }

    public void mouseExited(MouseEvent e)
    {

    }

If your methods are empty, don't give them so much space. Put them on 1 line per method. Don't put space between them. Ideally, put a comment above them why they are there ("required to implement from MouseListener but unneeded").


        StaticStuff.setShapeType(ShapeEnum.PENICL);

Typo?


class RectangleListener implements ActionListener
{

    @Override
    public void actionPerformed(ActionEvent event)
    {
        StaticStuff.setShapeType(ShapeEnum.RECTANGLE);
    }
}

class CircleListener implements ActionListener
{

    @Override
    public void actionPerformed(ActionEvent e)
    {
        StaticStuff.setShapeType(ShapeEnum.CIRCLE);
    }
}

class LineListener implements ActionListener
{

    @Override
    public void actionPerformed(ActionEvent e)
    {
        StaticStuff.setShapeType(ShapeEnum.LINE);
    }
}

class PencilListener implements ActionListener
{

    @Override
    public void actionPerformed(ActionEvent e)
    {
        StaticStuff.setShapeType(ShapeEnum.PENICL);
    }
}

class RubberListener implements ActionListener
{

    @Override
    public void actionPerformed(ActionEvent e)
    {
        StaticStuff.setShapeType(ShapeEnum.RUBBER);
    }
}

class FillClosedShapeListener implements ActionListener
{

    @Override
    public void actionPerformed(ActionEvent arg0)
    {
        StaticStuff.setShapeType(ShapeEnum.FILLCLOSEDSHAEP);
    }

}

Make 1 class. Call it ShapeListener. Give it a ShapeEnum. There, you've just saved tons of code duplication. Also... FILLCLOSEDSHAEP typo?


private Shape getTmpShape(int x, int y, int x2, int y2)
{
    switch (StaticStuff.getShapeType())
    {
    case RECTANGLE:
        return new Rectangle(x, y, x2, y2, StaticStuff.getShapeColor());
    case CIRCLE:
        return new Circle(x, y, x2, y2, StaticStuff.getShapeColor());
    case PENICL:
        return new Pencil(x, y, x2, y2, StaticStuff.getShapeColor());
    case LINE:
        return new Line(x, y, x2, y2, StaticStuff.getShapeColor());
    case RUBBER:
        return new Rubber(x, y, x2, y2, Color.WHITE);
    default:
        break;
    }
    return drawShape;
}

Personally, I would add this method in ShapeEnum. Doing so would group related changes together (if you have to add a new shape, all you want to be editing is your shapes class/enum).


    public in

t getY2()
{
    return y2;
}

Looks like something went wrong here.


That's what I pulled from it after a single passthrough. I don't have comments on your design yet. Possibly because nothing really looked badly out of place and there were more issues in the code than in the design.

\$\endgroup\$
4
  • \$\begingroup\$ Thanks for your response. I don't get two parts of your comments with class "ShapeListener" and "ShapeEnum". Could you provide some details, what do you mean? Now i have something like this: pastebin.com/7g5ZwiCr This is my class responsible for keep color and shape choose by user. So should i refactor only adding action listeners to buttons part? And FILLCLOSEDSHAEP is not a typo, i have buttons for 5 shapes and 6th button is for filling the closed shape with selected color using the flood fill algorithm. \$\endgroup\$
    – Pulkownik
    Jul 29, 2014 at 16:14
  • \$\begingroup\$ You spell shape like shaep? \$\endgroup\$
    – Pimgd
    Jul 29, 2014 at 22:03
  • \$\begingroup\$ I'd make 1 class called ShapeListener which has a constructor that takes ShapeEnum as argument. On ActionPerformed, it sets the StaticStuff.setShapeType to the ShapeType passed in during the constructor. \$\endgroup\$
    – Pimgd
    Jul 29, 2014 at 22:05
  • \$\begingroup\$ Ok now i get it :) thank you very much. My mistake it should be shape. Thanks! \$\endgroup\$
    – Pulkownik
    Jul 30, 2014 at 6:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.