反射“特殊属性/属性”,而不是Java中的getter/setter,以避免锅炉板代码
简介
我正在做一个开源项目Treez,我在树视图中组织所谓的“原子”。这些原子有时具有许多属性,这些属性可以通过树状视图中的用户操作或Eclipse代码编辑器中的API进行修改
原子本身的属性由可重用的“AttributeAtoms”表示。这些属性包含实际属性值,并提供验证等附加功能(“Atom”的其他可能术语可能是“widget”、“bean”、“property”或“tree node”)
问题
过去,我为每个Atom属性提供了一个getter/setter对。这是一项大量的额外工作,它会扩大我的Atom类的大小(参见下面的代码示例)。现在,我正在寻找一种替代解决方案
- 使创建新原子的工作量减少(维护原子的工作量也减少)李>
- 避免“冗余”getter/setter锅炉板代码李>
我将在下面描述几个选项。您会使用哪些选项?您对如何改进这些选项有何建议?你知道更多的选择吗
Getter/Setter代码示例
private AttributeAtom<String> myAttribute = new FilePathAttributeAtom("myAttribtue");
public String getMyAttribute() {
return myAttribute.getValue();
}
public void setMyAttribute(String value) {
this.myAtrribute.setValue(value);
}
相关文章
- does java have something similar to C# properties?
- (no) Properties in Java?
- http://blog.netopyr.com/2011/05/19/creating-javafx-properties/
- http://www.eclipse.org/forums/index.php/t/781816/
- Why use getters and setters?
- What is the advantage of having a private attribute with getters and setters?
考虑过的选项
A.使用IDE自动生成的getter/setter
Eclipse提供了自动生成getter/setter的可能性
- 不适用于我的AttributeAtoms,因为getter/setter代码看起来略有不同李>
- 不会避免额外的“冗余”代码李>
如果我决定保留getter/setter,我可以尝试为属性域创建类似的东西。另请参阅这篇关于JavaFx属性的自动getter/setter创建(不起作用)的文章: http://www.eclipse.org/forums/index.php/t/781816/
B.用于生成getter/setter的注释(Lombok项目)
Lombok提供了使用注释自动生成getter和setter的可能性
- 这两种方法都不适用于我的属性域
- 我尝试在Eclipse中使用Lombok。编辑器中的代码完成工作正常,但我收到了“未找到方法”警告。我可能需要投入更多的时间来让龙目巨人为经典属性工作李>
- 另见Is it safe to use Project Lombok?
如果我决定使用注释来定义getter/setter,那么就有可能扩展Lombok来处理我的AttributeAtoms
此处已存在针对JavaFx属性扩展Lombok的请求:https://groups.google.com/forum/#!searchin/project-lombok/getter$20and$20setter$20for$20properties/project-lombok/Ik6phxDXHVU/zzDkC2MpmvgJ
下面介绍如何使用自定义转换扩展Lambok: http://notatube.blogspot.de/2010/12/project-lombok-creating-custom.html
C.所有属性的一个通用getter/setter
我可以对所有Atom属性使用一个getter/setter对,比如
Object get(String attributeName)
void set(String attriuteName, Object value)
- 通过传递额外的类型参数,可以提高类型安全性李>
- 然而,Atom的代码完成只会建议使用单一的getter/setter,用户不会看到哪些属性可用。(也许可以通过使用枚举而不是字符串来标识属性来解决这个问题。但是需要以某种方式创建这些枚举。另请参见下一个选项。)李>
D.自定义Eclipse编辑器和代码处理
也许我可以为我的开源项目编写一个额外的Eclipse插件,通过建议相应的伪代码完成方法“允许访问私有属性”。在编译用户源代码之前,假调用
myAtom.setMyAttribue(newValue);
将转换为实际存在的通用getter的代码(选项C):
myAtom.set("myAttribute", newValue);
E.公共属性
如果将Atom属性公开,则不需要getter/sette每个原子中的r代码。相反,可重用的AttributeAtoms将提供get/set方法。例如,用法如下所示
myAtom.myAttribute.get();
myAtom.myAttribute.set(newValue);
而不是
myAtom.getMyAttribute();
myAtom.setMyAttribute(newValue);
一些缺点:
- 用户需要习惯这种“非常规方法”。Java用户可能期望
setMyAttribute(newValue)
,而C#用户可能期望myAtom.myAttribute = newValue
李> 可以交换我不允许的整个属性TOM:
myAtom.myAttribute = completelyDifferentAttribute
有什么改进的策略吗
是否有一种方法允许访问属性的方法,而不允许交换属性本身?我需要一个新的访问修饰符,如
private *publicMethodAccess* AttributeAtom<String> myAttribute;
原子代码示例
下面是一个示例Atom类。如果滚动到底部,您将发现getter/setter使用的许多代码行。这很难看,不是吗
package org.treez.results.atom.probe;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import org.eclipse.swt.graphics.Image;
import org.treez.core.atom.attribute.AttributeRoot;
import org.treez.core.atom.attribute.ModelPath;
import org.treez.core.atom.attribute.ModelPathSelectionType;
import org.treez.core.atom.attribute.Section;
import org.treez.core.atom.attribute.base.AttributeAtom;
import org.treez.core.atom.variablerange.VariableRange;
import org.treez.core.data.column.ColumnType;
import org.treez.data.column.Columns;
import org.treez.data.output.OutputAtom;
import org.treez.data.table.Table;
import org.treez.results.Activator;
/**
* Collects data from a sweep and puts it in a single (probe-) table. That table can easier be used to produce plots
* than the distributed sweep results.
*/
public class SweepProbe extends AbstractProbe {
/**
* Logger for this class
*/
@SuppressWarnings("unused")
private static Logger sysLog = Logger.getLogger(SweepProbe.class);
//#region ATTRIBUTES
private AttributeAtom<String> xLabel;
private ModelPath xRange;
private AttributeAtom<String> yLabel;
private AttributeAtom<String> firstFamilyLabel;
private ModelPath firstFamilyRange;
private AttributeAtom<String> secondFamilyLabel;
private ModelPath secondFamilyRange;
private AttributeAtom<String> probeName;
private ModelPath sweepOutputModel;
private ModelPath firstProbeTable;
private AttributeAtom<String> probeColumnIndex;
private AttributeAtom<String> probeRowIndex;
//#end region
//#region CONSTRUCTORS
/**
* Constructor
*
* @param name
*/
public SweepProbe(String name) {
super(name);
createPropertyModel();
}
//#end region
//#region METHODS
/**
* Creates the model for the property control
*/
private void createPropertyModel() {
//root
AttributeRoot root = new AttributeRoot("root");
//page
org.treez.core.atom.attribute.Page page = root.createPage("page");
//x section
Section xSection = page.createSection("xSection", "X");
xSection.createSectionAction("action", "Run probe", () -> execute(treeViewRefreshable));
xLabel = xSection.createTextField("xLabel", "Label for x-Axis", "x");
xRange = xSection.createModelPath("xRange", "Range for x-Axis", "", VariableRange.class, this);
xRange.setSelectionType(ModelPathSelectionType.FLAT);
xRange.setValue("root.studies.sweep.threshold");
//y section
Section ySection = page.createSection("ySection", "Y");
yLabel = ySection.createTextField("yLabel", "Label for y-Axis", "y");
//first family section
Section firstFamilySection = page.createSection("firstFamily", "First family");
firstFamilySection.setExpanded(false);
firstFamilyLabel = firstFamilySection.createTextField("firstFamilyLabel", "Label for first family", "family1");
firstFamilyRange = firstFamilySection.createModelPath("firstFamilyRange", "Range for first family", "",
VariableRange.class, this);
//second family section
Section secondFamilySection = page.createSection("secondFamily", "Second family");
secondFamilySection.setExpanded(false);
secondFamilyLabel = secondFamilySection.createTextField("secondFamilyLabel", "Label for second family",
"family2");
secondFamilyRange = secondFamilySection.createModelPath("secondFamilyRange", "Range for second family", "",
VariableRange.class, this);
//probe section
Section probeSection = page.createSection("probe", "Probe");
probeName = probeSection.createTextField("propeName", "Name", "MyProbe");
sweepOutputModel = probeSection.createModelPath("sweepOutput", "SweepOutput", "", OutputAtom.class, this);
firstProbeTable = probeSection.createModelPath("tablePath", sweepOutputModel, Table.class);
firstProbeTable.setLabel("First probe table");
probeColumnIndex = probeSection.createTextField("probeColumnIndex", "Column index", "0");
probeRowIndex = probeSection.createTextField("probeColumnIndex", "Row index", "0");
setModel(root);
}
/**
* Provides an image to represent this atom
*/
@Override
public Image provideBaseImage() {
Image baseImage = Activator.getImage("sweep.png");
return baseImage;
}
//#region CREATE TABLE COLUMNS
/**
* Creates the required columns for the given table
*
* @param table
*/
@Override
protected void createTableColumns(Table table) {
//TODO
}
//#end region
//#region COLLECT PROBE DATA
@Override
protected void collectProbeDataAndFillTable() {
// TODO Auto-generated method stub
}
//#end region
//#end region
//#region ACCESSORS
//#region X LABEL
/**
* @return
*/
public String getXLabel() {
return xLabel.getValue();
}
/**
* @param label
*/
public void setXLabel(String label) {
xLabel.setValue(label);
}
//#end region
//#region X RANGE
/**
* @return
*/
public String getXRange() {
return xRange.getValue();
}
/**
* @param range
*/
public void setXRange(String range) {
xRange.setValue(range);
}
//#end region
//#region Y LABEL
/**
* @return
*/
public String getYLabel() {
return yLabel.getValue();
}
/**
* @param label
*/
public void setYLabel(String label) {
yLabel.setValue(label);
}
//#end region
//#region FIRST FAMILY LABEL
/**
* @return
*/
public String getFirstFamilyLabel() {
return firstFamilyLabel.getValue();
}
/**
* @param label
*/
public void setFirstFamilyLabel(String label) {
firstFamilyLabel.setValue(label);
}
//#end region
//#region FIRST FAMILY RANGE
/**
* @return
*/
public String getFirstFamilyRange() {
return firstFamilyRange.getValue();
}
/**
* @param range
*/
public void setFirstFamilyRange(String range) {
firstFamilyRange.setValue(range);
}
//#end region
//#region SECOND FAMILY LABEL
/**
* @return
*/
public String getSecondFamilyLabel() {
return secondFamilyLabel.getValue();
}
/**
* @param label
*/
public void setSecondFamilyLabel(String label) {
secondFamilyLabel.setValue(label);
}
//#end region
//#region SECOND FAMILY RANGE
/**
* @return
*/
public String getSecondFamilyRange() {
return secondFamilyRange.getValue();
}
/**
* @param range
*/
public void setSecondFamilyRange(String range) {
secondFamilyRange.setValue(range);
}
//#end region
//#region PROBE
/**
* @return
*/
public String getProbeName() {
return probeName.getValue();
}
/**
* @param name
*/
public void setProbeName(String name) {
probeName.setValue(name);
}
//#end region
//#region SWEEP OUTPUT MODEL
/**
* @return
*/
public String getSweepOutputModelName() {
return sweepOutputModel.getValue();
}
/**
* @param sweepOutputModel
*/
public void setSweepOutputModelName(String sweepOutputModel) {
this.sweepOutputModel.setValue(sweepOutputModel);
}
//#end region
//#region PROBE TABLE
/**
* @return
*/
public String getFirstProbeTable() {
return firstProbeTable.getValue();
}
/**
* @param firstProbeTable
*/
public void setFirstProbeTable(String firstProbeTable) {
this.firstProbeTable.setValue(firstProbeTable);
}
//#end region
//#region COLUMN INDEX
/**
* @return
*/
public String getProbeColumnIndex() {
return probeColumnIndex.getValue();
}
/**
* @param index
*/
public void setProbeColumnIndex(String index) {
probeColumnIndex.setValue(index);
}
//#end region
//#region ROW INDEX
/**
* @return
*/
public String getProbeRowIndex() {
return probeRowIndex.getValue();
}
/**
* @param index
*/
public void setProbeRowIndex(String index) {
probeRowIndex.setValue(index);
}
//#end region
//#end region
}
# 1 楼答案
几年后,我想补充一点,我完全转向了JavaScript。 对于我的用例来说,这似乎是更好的语言
# 2 楼答案
对于选项E,通过使用“final”修饰符,可以防止替换一个全新的AttributeAtom,同时仍然允许获取/设置:
然后将允许以下情况:
但你担心的不是:
# 3 楼答案
我最终决定使用一种新的模式,它通过“双重包装”扩展选项E:
最后的工作流程是
所有这些都将通过一些示例代码变得更加清晰:
最终用法
在MyAtom的类定义中使用Attribute和Wrap
助手方法在AttributeAtom中“包裹”
接口属性
接口属性包装器
Wrap:AttributeWrapper的实现