#java #шаблоны_проектирования
В ходе изучения языка все реже возникают вопросы "Как решить задачу?" и все чаще встает вопрос "Какой способ более правильный с точки зрения хорошего тона программирования?" К примеру, в ходе написания программы, реализующей возможность ведения складского учета, возник вопрос, какой класс использовать: анонимный, внешний или внутренний? Есть класс главного окна программы, реализующий JDesktopPane. Есть отдельный класс "Каталог товаров", реализующий JInternalFrame. Из последнего, нажатием кнопок, открывается окно Карточки товара" (также класс extends JInternalFrame). В свою очередь кликом по кнопкам он реализует открытие окон выбора группы товара, единицы измерения, категории и т.д. Все это тоже JInternalFrame. Вот теперь стоит вопрос, какой тип класса "эстетичнее" использовать? Логика подсказывает делать все классы ниже "Каталога товаров" вложенными в него, а все слушатели нажатия кнопок - анонимными. Но тогда класс получается под тысячу строк кода, что влияет на читаемость. Или это нормально? В общем я как тот осел, между двумя стогами сена. Вот, к примеру, такой участок кода. Оптимален ли он, или стоит изменить структуру? package catalogs; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.beans.PropertyVetoException; import java.io.File; import java.util.ArrayList; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JDesktopPane; import javax.swing.JFrame; import javax.swing.JInternalFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.JTree; import javax.swing.ListSelectionModel; import javax.swing.event.TableModelListener; import javax.swing.event.TreeModelListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.table.TableModel; import javax.swing.tree.TreeModel; import javax.swing.tree.TreePath; import main.Global; /** * Класс окна справочника товара. Выводит окно каталога товаров с возможностью добавления/удаления/редактирования групп товаров и самих * товаров. Представляет собой древо групп товаров, таблицу товаров, кнопки добавления/удаления/редактирования. */ public class ProductCatalog extends JInternalFrame{ private static final long serialVersionUID = 1L; private JDesktopPane mainPane; public JTree treeGroup; private GroupTreeModel treeGroupModel; private JScrollPane treeGroupScrlPane; private JButton btnAddGroup; private JButton btnDelGroup; private JButton btnEditGroup; public JTable tblProduct; public ProdTableModel tblProductModel; private JScrollPane tblProductScrlPane; private JButton btnAddProduct; private JButton btnDelProduct; private JButton btnEditProduct; /** * Конструктор, принимает ссылку на JDesctopPane главного окна. */ public ProductCatalog(JDesktopPane mainPane){ this.mainPane = mainPane; paintComponents(); } /** * Метод отрисовки компонент окна. */ private void paintComponents(){ treeGroupModel = new GroupTreeModel(); treeGroup = new JTree(treeGroupModel); treeGroup.setSelectionRow(0); treeGroup.addTreeSelectionListener(new TreeSelectionListener(){ @Override public void valueChanged(TreeSelectionEvent selection) { if(!treeGroup.isSelectionEmpty()){ tblProductModel.buildTable(treeGroup.getLastSelectedPathComponent()); tblProduct.revalidate(); tblProduct.repaint(); } } }); treeGroupScrlPane = new JScrollPane(treeGroup); treeGroupScrlPane.setBounds(10, 10, 150, 260); btnAddGroup = new JButton(); ImageIcon btnAddGroupIcon = new ImageIcon(Global.getAppPath()+File.separator+"images"+File.separator+"btnAddGroup.png"); btnAddGroup.setIcon(btnAddGroupIcon); btnAddGroup.addActionListener(new ActionListener(){ @Override public void actionPerformed(ActionEvent e) { addGroup(); } }); btnAddGroup.setBounds(10, 280, 30, 30); btnDelGroup = new JButton(); ImageIcon btnDelGroupIcon = new ImageIcon(Global.getAppPath()+File.separator+"images"+File.separator+"btnDelGroup.png"); btnDelGroup.setIcon(btnDelGroupIcon); btnDelGroup.addActionListener(new ActionListener(){ @Override public void actionPerformed(ActionEvent e) { delGroup(); } }); btnDelGroup.setBounds(50, 280, 30, 30); btnEditGroup = new JButton(); ImageIcon btnEditGroupIcon = new ImageIcon(Global.getAppPath()+File.separator+"images"+File.separator+"btnEditGroup.png"); btnEditGroup.setIcon(btnEditGroupIcon); btnEditGroup.addActionListener(new ActionListener(){ @Override public void actionPerformed(ActionEvent e) { editGroup(); } }); btnEditGroup.setBounds(90, 280, 30, 30); tblProductModel = new ProdTableModel(); tblProduct = new JTable(tblProductModel); tblProductModel.buildTable(treeGroup.getLastSelectedPathComponent()); tblProduct.setAutoCreateRowSorter(true); tblProduct.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); tblProduct.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); tblProduct.getColumnModel().getColumn(0).setPreferredWidth(25); tblProduct.getColumnModel().getColumn(1).setPreferredWidth(200); tblProduct.getColumnModel().getColumn(2).setPreferredWidth(25); tblProduct.getColumnModel().getColumn(3).setPreferredWidth(50); tblProductScrlPane = new JScrollPane(tblProduct); tblProductScrlPane.setBounds(170, 10, 500, 260); btnAddProduct = new JButton(); ImageIcon btnAddProductIcon = new ImageIcon(Global.getAppPath()+File.separator+"images"+File.separator+"btnAddProduct.png"); btnAddProduct.setIcon(btnAddProductIcon); btnAddProduct.addActionListener(new ActionListener(){ @Override public void actionPerformed(ActionEvent e) { addProduct(); } }); btnAddProduct.setBounds(170, 280, 30, 30); btnDelProduct = new JButton(); ImageIcon btnDelProductIcon = new ImageIcon(Global.getAppPath()+File.separator+"images"+File.separator+"btnDelProduct.png"); btnDelProduct.setIcon(btnDelProductIcon); btnDelProduct.addActionListener(new ActionListener(){ @Override public void actionPerformed(ActionEvent e) { delProduct(); } }); btnDelProduct.setBounds(210, 280, 30, 30); btnEditProduct = new JButton(); ImageIcon btnEditProductIcon = new ImageIcon(Global.getAppPath()+File.separator+"images"+File.separator+"btnEditProduct.png"); btnEditProduct.setIcon(btnEditProductIcon); btnEditProduct.addActionListener(new ActionListener(){ @Override public void actionPerformed(ActionEvent e) { editProduct(); } }); btnEditProduct.setBounds(250, 280, 30, 30); this.setTitle("Каталог товаров"); this.addComponentListener(new ComponentListener(){ @Override public void componentResized(ComponentEvent e) { treeGroupScrlPane.setBounds(10, 10, 150, ProductCatalog.this.getHeight()-87); btnAddGroup.setBounds(10, ProductCatalog.this.getHeight()-67, 30, 30); btnDelGroup.setBounds(50, ProductCatalog.this.getHeight()-67, 30, 30); btnEditGroup.setBounds(90, ProductCatalog.this.getHeight()-67, 30, 30); tblProductScrlPane.setBounds(170, 10, ProductCatalog.this.getWidth()-200, ProductCatalog.this.getHeight()-87); btnAddProduct.setBounds(170, ProductCatalog.this.getHeight()-67, 30, 30); btnDelProduct.setBounds(210, ProductCatalog.this.getHeight()-67, 30, 30); btnEditProduct.setBounds(250, ProductCatalog.this.getHeight()-67, 30, 30); } @Override public void componentHidden(ComponentEvent arg0) {} @Override public void componentMoved(ComponentEvent arg0) {} @Override public void componentShown(ComponentEvent arg0) {} }); this.setClosable(true); this.setMaximizable(true); this.setIconifiable(true); this.setResizable(true); this.getContentPane().setLayout(null); this.setSize(700, 347); this.setVisible(true); this.add(treeGroupScrlPane); this.add(btnAddGroup); this.add(btnDelGroup); this.add(btnEditGroup); this.add(tblProductScrlPane); this.add(btnAddProduct); this.add(btnDelProduct); this.add(btnEditProduct); } /** * Рекурсивный метод построения массива элементов древа групп товаров. */ ArrayListarr = new ArrayList (); public ArrayList getComboBoxTree(Object parent, String separator){ Object[][] buf = Global.sqlQueryResult("SELECT * FROM `prod_group` WHERE `parent`='"+parent+"'"); for(int i=0; i 0) getComboBoxTree(buf[i][0], " "+separator); } return arr; } /** * Метод, реализующий добавление группы товаров в БД. */ private void addGroup(){ String name = JOptionPane.showInputDialog(null, "Введите название новой подгруппы\n в группе \""+ treeGroup.getLastSelectedPathComponent() +"\":", ""); if(name!=null){ Object[][] buf = Global.sqlQueryResult("SELECT * FROM `prod_group` WHERE `name`='"+ name +"'"); if(buf.length==0){ if(name.length()>0){ Object[][] parentGroup = Global.sqlQueryResult("SELECT * FROM `prod_group` WHERE `name`='"+ treeGroup.getLastSelectedPathComponent() +"'"); Global.sqlQueryVoid("INSERT INTO `prod_group` (`name`, `parent`) VALUES ('"+ name +"', '"+ parentGroup[0][0] +"')"); treeGroup.updateUI(); }else JOptionPane.showMessageDialog(null, "Наименование группы товаров не может быть пустым!", "Ошибка!", JOptionPane.ERROR_MESSAGE); }else JOptionPane.showMessageDialog(null, "Группа товаров с таким наименованием уже существует!", "Ошибка!", JOptionPane.ERROR_MESSAGE); } } /** * Метод, реализующий удаление группы товаров из БД. */ private void delGroup(){ boolean isEmpty = false; Object[][] buf = Global.sqlQueryResult("SELECT `id` FROM `prod_group` WHERE `name`='"+ treeGroup.getLastSelectedPathComponent() +"'"); Object[][] buf2 = Global.sqlQueryResult("SELECT * FROM `product` WHERE `group`='"+ buf[0][0] +"'"); Object[][] buf3 = Global.sqlQueryResult("SELECT * FROM `prod_group` WHERE `parent`='"+ buf[0][0] +"'"); if(buf2.length==0 && buf3.length==0) isEmpty = true; if(!treeGroup.getLastSelectedPathComponent().equals(null)){ //Если выделена группа, if(isEmpty){ //она пуста if(JOptionPane.showConfirmDialog(null, "Вы уверены?", "Подтверждение удаления", JOptionPane.YES_NO_OPTION)==0){ //и мы подтверждаем удаление, тогда удаляем. Global.sqlQueryVoid("DELETE FROM `prod_group` WHERE `name`='"+ treeGroup.getLastSelectedPathComponent() +"'"); treeGroup.updateUI(); } }else JOptionPane.showMessageDialog(null, "Нельзя удалить эту папку, т.к. она не пуста!", "Ошибка!", JOptionPane.ERROR_MESSAGE); }else JOptionPane.showMessageDialog(null, "Вы не выбрали ни одну папку!", "Ошибка!", JOptionPane.ERROR_MESSAGE); } /** * Метод, реализующий редактирование группы товаров в БД. */ private void editGroup(){ if(treeGroup.getSelectionRows()[0] != 0){ //Если выбрана не корневая группа, переименовываем группу товаров. final JInternalFrame ifEditGroup; final JComboBox cbParent; final JLabel lblName; final JTextField tfName; final JButton btnOK; final JButton btnCancel; final ArrayList arr; ifEditGroup = new JInternalFrame(); JLabel lblParent = new JLabel("Родительская группа:"); lblParent.setBounds(10, 10, 150, 20); cbParent = new JComboBox (); cbParent.setBounds(170, 10, 250, 20); arr = getComboBoxTree(0, ""); for(int i=0; i getColumnClass(int colInd) { return String.class; } @Override public int getColumnCount() { return 4; } @Override public String getColumnName(int colInd) { String colName =""; switch(colInd){ case 0: colName = "Код"; break; case 1: colName = "Наименование"; break; case 2: colName = "Ед. изм."; break; case 3: colName = "Комментарий"; break; } return colName; } @Override public int getRowCount() { return sample.length; } @Override public Object getValueAt(int rowInd, int colInd) { return sample[rowInd][colInd]; } @Override public void addTableModelListener(TableModelListener arg0) {} @Override public boolean isCellEditable(int arg0, int arg1) {return false;} @Override public void removeTableModelListener(TableModelListener arg0) {} @Override public void setValueAt(Object arg0, int arg1, int arg2) {} } }
Ответы
Ответ 1
Анонимные классы удобны там, где цель существования типа - применение в одной единственной точке на всё приложение. Внутренний (нестатический) класс существует внутри внешнего и может быть там скрыт. Кроме того сам имеет доступ к скрытому состоянию родителя. Если такие "суперспособности" не принесут ощутимой пользы, то стоит склоняться к отдельным классам. Отдельные классы читаются лучше (и соответствуют Single responsibility) при прочих равных. А вообще, это достаточно тонкая грань между хорошим кодом и плохим. Чувство прекрасного развивается при чтении чужого кода, где ты распутывая клубок алгоритма подмечаешь для себя какие-то элегантные решения и злишься на излишнюю запутанность других.
Комментариев нет:
Отправить комментарий