如何设计一个屏幕来处理许多(类似的,子类)对象?

前言:我试图在不需要样板代码的情况下解决我的问题。 我可以通过复制/粘贴和重构代码轻松解决这个问题,但是这种问题应该可以用模型驱动的方法解决,因为设计明显重现。

我有一个包含四种不同对象类型的数据库应用程序,它们将用作另一个中心对象的参数。

在此处输入图像描述

所有四个字段在结构上都是相同的:一个自动生成的主键和一个VARCHAR字段。 这些字段需要由最终用户编辑,因此我设计了这个屏幕:

在此处输入图像描述

我没有为此制作四个不同的屏幕,而是认为制作这个由四种不同对象类型使用的屏幕会很好。 为了实现这一点,我(显然,对吗?)需要创建一个抽象对象(我称之为) StringField ,它可以通过四种子类型实现: StatusTypeClassificationDesignation 。 如前所述,因为每个对象在结构上都是相同的,并且以相同的方式读/写数据库,所以抽象对象似乎是合乎逻辑的。 所以,我做了这个对象:

 public abstract class StringField { private final int id; private final String value; private final String fieldname; private final String tablename; public StringField(int id, String value, String fieldname, String tablename) { this.id = id; this.value = value; this.fieldname = fieldname; this.tablename = tablename; } public String getValue() { return value; } public int getId() { return id; } @Override public int hashCode() { int hash = 5; hash = 59 * hash + this.id; return hash; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } return this.id == ((StringField)obj).id; } @Override public String toString() { return value; } public int insertValue(DB db, String value) throws WrappedSQLException { ... // DB code here } public void updateValue(DB db, String value, int id) throws WrappedSQLException { ... // DB code here } public void deleteValue(DB db, int id) throws WrappedSQLException { ... // DB code here } } 

子类实现如下(例如):

 public class Type extends StringField { public Type(int id, String value) { super(id, value, "type", "customer_type"); } } public class Classification extends StringField { public Classification (int id, String value) { super(id, value, "classification", "customer_classification"); } } 

旁白:构造函数中String fieldnameString tablename的原因是Java不允许静态抽象字段。 因此,它们需要由使用超类型的类手动提供。 遗憾的是,这会阻止insertupdatedelete方法被声明为static 。 如果Java允许static abstract ,我怀疑我会问这个问题,因为我只是使用fieldnametablename作为抽象静态字段使insertupdatedelete方法静态,这将被强制由子类型实现以静态方式,然后可以由超类静态引用,然后可以通过每个子类型静态调用。 基本上,它可能看起来像这样:

超类型:

 public abstract class StringField { private final int id; private final String value; public StringField(int id, String value) { this.id = id; this.value = value; } public static abstract String fieldname; public static abstract String tablename; public static int insertValue(String value) { // Insert code, returns auto-generated primary key // ... // This code will use the static fields `fieldname` and `tablename`. // ... return -1; } public static void updateValue(String value, int id) { // Update code... // ... // This code will use the static fields `fieldname` and `tablename`. // ... } public static void deleteValue(int id) { // Delete code... // ... // This code will use the static fields `fieldname` and `tablename`. // ... } } 

子类型:

 static class Type extends StringField { public Type(int id, String value) { super(id, value); } public static String fieldname = "type"; public static String tablename = "customer_type"; } 

使用如下:

 Type.insertValue(newValue); 

好吧,足够抱怨Java不允许static abstract 。 这是我到目前为止完整的,(几乎)可编译的代码,它说明了我的方法:

 @SuppressWarnings("serial") public class StringFieldEditor extends JPanel { final Collection fields; String fieldname; public StringFieldEditor(Collection fields, String fieldname) { super(new GridBagLayout()); this.fields = fields; this.fieldname = fieldname; GridBagConstraints gridBagConstraints; JButton addNew_button = new JButton(); JButton delete_button = new JButton(); JButton edit_button = new JButton(); JLabel jLabel1 = new JLabel(); JList jList = new JList(); jList.setModel(new DefaultListModel()); jList.setListData(fields.toArray(new StringField[fields.size()])); JScrollPane jScrollPane1 = new JScrollPane(); jScrollPane1.setViewportView(jList); jLabel1.setText("Customer " + fieldname + " Editor"); gridBagConstraints = new GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 0; gridBagConstraints.gridwidth = 2; gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; gridBagConstraints.insets = new Insets(11, 10, 0, 0); add(jLabel1, gridBagConstraints); gridBagConstraints = new GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 1; gridBagConstraints.gridwidth = 5; gridBagConstraints.fill = GridBagConstraints.BOTH; gridBagConstraints.ipadx = 178; gridBagConstraints.ipady = 206; gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; gridBagConstraints.weightx = 1.0; gridBagConstraints.weighty = 1.0; gridBagConstraints.insets = new Insets(6, 10, 0, 10); add(jScrollPane1, gridBagConstraints); addNew_button.setText("Add New"); addNew_button.addActionListener((ActionEvent evt) -> { // Add New Task }); gridBagConstraints = new GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 2; gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; gridBagConstraints.insets = new Insets(6, 10, 11, 0); add(addNew_button, gridBagConstraints); edit_button.setText("Edit"); edit_button.addActionListener((ActionEvent evt) -> { // Edit Task }); gridBagConstraints = new GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 2; gridBagConstraints.gridwidth = 3; gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; gridBagConstraints.insets = new Insets(6, 6, 11, 0); add(edit_button, gridBagConstraints); delete_button.setText("Delete"); delete_button.addActionListener((ActionEvent evt) -> { // Delete Task }); gridBagConstraints = new GridBagConstraints(); gridBagConstraints.gridx = 4; gridBagConstraints.gridy = 2; gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; gridBagConstraints.insets = new Insets(6, 6, 11, 10); add(delete_button, gridBagConstraints); } static class Type extends StringField { public Type(int id, String value) { super(id, value, "type", "customer_type"); } } public abstract class StringField { private final int id; private final String value; private final String fieldname; private final String tablename; public StringField(int id, String value, String fieldname, String tablename) { this.id = id; this.value = value; this.fieldname = fieldname; this.tablename = tablename; } public String getValue() { return value; } public int getId() { return id; } @Override public int hashCode() { int hash = 5; hash = 59 * hash + this.id; return hash; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } return this.id == ((StringField)obj).id; } @Override public String toString() { return value; } public int insertValue(String value) { // Insert code, returns auto-generated primary key // ... // ... return -1; } public void updateValue(String value, int id) { // Update code... // ... // ... } public void deleteValue(int id) { // Delete code... // ... // ... } } public static void main(String[] args) { LinkedList types = new LinkedList(); types.add(new Type(1, "Type 1")); types.add(new Type(2, "Type 2")); types.add(new Type(3, "Type 3")); JFrame frame = new JFrame(); frame.add(new StringFieldEditor(types, "type")); frame.pack(); frame.setLocationByPlatform(true); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } } 

但是,这会引发错误:

 incompatible types: LinkedList cannot be converted to Collection 

但改变:

  final Collection fields; String fieldname; public StringFieldEditor(Collection fields, String fieldname) { 

  final Collection fields; String fieldname; public StringFieldEditor(Collection fields, String fieldname) { 

尝试将数据加载到JList时导致问题( toArray需要类型T值,但如果我不知道我正在使用的确切类型,我不能提供新的T类型数组。

此外,我预见到尝试以非子类特定方式引用非静态插入,更新和删除方法的问题。

我的问题:

  1. 这种方法可行吗? 我是否将整个模型驱动的开发方法推得太远了?
  2. 有没有更好的方法来实现此function而无需创建样板代码?
  3. 有一个很好的解决方法,因为Java中没有static abstract方法/字段吗?
  4. 当你不知道你将使用哪个子类型时,有没有办法为Collections::toArray(T[])方法提供一个可用的数组?