"Special attributes/properties" 而不是 Java 中的 getter/setter 以避免样板代码
"Special attributes/properties" instead of getter/setter in Java to avoid boiler plate code
简介
我正在开发一个开源项目 Treez,我在该项目中以树视图组织所谓的 "Atoms"。这些 Atom 有时有 很多属性 并且这些属性可以通过树视图中的用户操作或通过 API一个 Eclipse 代码编辑器。
我的原子本身的属性由可重用"AttributeAtoms"表示。那些保存实际属性值并提供附加功能,如验证("Atom" 的其他可能术语可能是 "widget"、"bean"、"property" 或 "tree node")。
问题
过去我为每个 Atom 属性提供了一个 getter/setter 对。这是很多额外的工作,它会增加我的 Atom classes 的大小(请参阅下面的代码示例)。现在我正在寻找替代解决方案
- 减少创建新 Atom 的工作量(并且减少维护它们的工作量)。
- 避免了 "redundant" 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?
已考虑的选项
一个。使用 IDE
自动生成 getters/setters
Eclipse 提供了自动生成 getters/setters.
的可能性
- 不适用于我的 AttributeAtoms,因为 gettter/setter 代码看起来略有不同。
- 不避免额外的"redundant"代码。
如果我决定保留 getters/setters,我可以尝试为我的 AttributeAtoms 创建类似的东西。另请参阅此 post 关于 JavaFx 属性的(不工作)自动 getter/setter 创建:
http://www.eclipse.org/forums/index.php/t/781816/
乙。生成注释 getters/setters (Project Lombok)
Lombok 提供了使用注解自动生成 getters 和 setter 的可能性。
- 对我的 AttributeAtoms 都不起作用
- 我尝试将 Lombok 与 Eclipse 一起使用。编辑器中的代码完成工作有效,但我收到 "method not found" 警告。我可能需要投入更多时间让 Lombok 为 classical 属性工作。
- 另见 Is it safe to use Project Lombok?
如果我决定使用注释来定义 getters/setters,则可以扩展 Lombok 来为我的 AttributeAtoms 工作。
为 JavaFx 属性扩展 Lombok 的请求已存在于此处:https://groups.google.com/forum/#!searchin/project-lombok/getterandsetterforproperties/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 插件,"allows to access private attributes" 通过建议相应的伪造代码完成方法。在编译用户源代码之前,像
这样的假调用
myAtom.setMyAttribue(newValue);
将被翻译成真正存在的通用代码 getter(选项 C):
myAtom.set("myAttribute", newValue);
E. Public 属性
如果我将我的 Atom 属性设置为 public,我就不需要在每个 Atom 中添加 getters/setter 代码。相反,可重用的 AttributeAtoms 将提供 get/set 方法。例如,用法看起来像这样
myAtom.myAttribute.get();
myAtom.myAttribute.set(newValue);
而不是
myAtom.getMyAttribute();
myAtom.setMyAttribute(newValue);
一些缺点:
- 用户需要习惯这一点"unconventional approach"。 Java 用户可能期望
setMyAttribute(newValue)
,而 C# 用户可能期望 myAtom.myAttribute = newValue
。
可以交换整个 AttributeAtom,我不想允许:
myAtom.myAttribute = completelyDifferentAttribute
有什么改进策略吗?
有没有办法允许访问属性的方法,同时不允许交换属性本身?我需要一个新的访问修饰符,比如
private *publicMethodAccess* AttributeAtom<String> myAttribute;
Atom 代码示例
这里有一个 Atom 的例子 class。如果滚动到底部,您会发现 getters/setters 使用了很多代码行。这很丑,不是吗?
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
}
对于选项 E,通过使用 "final" 修饰符,您可以防止换入一个全新的 AttributeAtom,同时仍然允许 getting/setting:
public final AttributeAtom<String> myAttribute = new FilePathAttributeAtom("myAttribtue");
然后将允许以下内容:
myAtom.myAttribute.get();
myAtom.myAttribute.set(newValue)
但你担心的事情不会是:
myAtom.myAttribute = completelyDifferentAttribute
我最终决定使用一种新模式,它用 "double wrapping":
扩展选项 E
- 有一个接口 "Attribute" 提供方法 "T get()" 和 "set(T value)"
- 另一个接口 "AttributeWrapper" 继承自 "Attribute"。它提供了额外的方法 "setAttribute" 和 "getAttribute" 来交换包装的属性。
- AttributeWrapper 的方法 get/set 被重定向到包装的属性。
- 如果 "AttributeWrapper" 作为 "Attribute" 传递给外界,只有方法 "get" 和 "set" 可见。这提供了一些(伪)封装。 (这种封装可能几乎与使用 private 修饰符的封装一样好。无论如何,private 修饰符都可以通过反射绕过。)
- 那些知道我的属性实际上是 AttributeWrappers 的人能够将属性转换为 AttributeWrappers 并交换包装的属性。这也允许我不仅在构造函数中而且在 Atom classes 的任何方法中创建和交换我的 public final 属性。 (将所有属性定义直接放在属性区域或构造函数中会使我的代码难看,因此难以阅读。)
- 有一个 class "Wrap" 提供了 AttributeWrapper 的默认实现。
- 抽象 class AttributeAtom 是我所有属性原子的基础 class。它实现了 Attribute,还提供了一个辅助方法 "wrap"。该方法将 AttributeAtom 包装在传递给该方法的父 AttributeWrapper 中。
最终的工作流程是
- 声明一个 public 最终属性 myAttribute 并立即为其分配一个 Wrap。
- 在初始化方法中创建实际的属性 newAttribute。
- 使用辅助方法"wrap".
将新的Attribute指定为相应Wrap的内容
所有这些都将通过一些示例代码变得更加清晰:
最终使用
MyAtom myAtom = new MyAtom();
String defaultPath = myAtom.myFilePathAttribute.get();
myAtom.myFilePathAttribute.set("D:/newpath.txt")
MyAtomclass 定义的属性和包装的使用
public class MyAtom {
//#region ATTRIBUTES
public final Attribute<String> myFilePathAttribute = new Wrap<>();
//#end region
//...
//#region METHODS
private init(){
//create a new AttributeAtom
//(FilePath inherits from AttributeAtom<String>)
FilePath filePath = new FilePath("C:/defaultpath.txt");
//set the new AttributeAtom as content of the corresponding Wrap myFilePathAttribute
filePath.wrap(myFilePathAttribute);
}
//#end region
}
AttributeAtom
中的辅助方法"wrap"
/**
* Wraps this attribute in the AttributeWrapper that is given as Attribute
*
* @param wrap
*/
public void wrap(Attribute<T> wrap) {
Wrap<T> wrapper = (Wrap<T>) wrap;
wrapper.setAttribute(this);
}
接口属性
package org.treez.core.attribute;
/**
* Represents an attribute
*
* @param <T>
*/
public interface Attribute<T> {
/**
* Returns the attribute value
*
* @return
*/
T get();
/**
* Sets the attribute value
*
* @param value
*/
void set(T value);
}
接口属性包装器
package org.treez.core.attribute;
/**
* Wraps a replaceable attribute. The methods are "hidden" if this AttributeWrapper is passed as its parent interface
* Attribute
*
* @param <T>
*/
public interface AttributeWrapper<T> extends Attribute<T> {
/**
* Sets the wrapped Attribute
*
* @param attribute
*/
void setAttribute(Attribute<T> attribute);
/**
* Returns the wrapped Attribute
*
* @return
*/
Attribute<T> getAttribute();
}
Wrap: AttributeWrapper
的实现
package org.treez.core.attribute;
import java.util.Objects;
/**
* Default implementation of the AttributeWrapper interface
*/
public class Wrap<T> implements AttributeWrapper<T> {
//#region ATTRIBUTES
private Attribute<T> wrappedAttribute;
//#end region
//#region CONSTRUCTORS
/**
* Constructor
*/
public Wrap() {}
/**
* Constructor with wrapped attribute
*
* @param wrappedAttribute
*/
public Wrap(Attribute<T> wrappedAttribute) {
this.wrappedAttribute = wrappedAttribute;
}
//#end region
//#region ACCESSORS
@Override
public T get() {
Objects.requireNonNull(wrappedAttribute, "Wrapped attribute must be set before calling this method.");
T value = wrappedAttribute.get();
return value;
}
@Override
public void set(T value) {
Objects.requireNonNull(wrappedAttribute, "Wrapped attribute must be set before calling this method.");
wrappedAttribute.set(value);
}
@Override
public Attribute<T> getAttribute() {
return wrappedAttribute;
}
@Override
public void setAttribute(Attribute<T> wrappedAttribute) {
this.wrappedAttribute = wrappedAttribute;
}
//#end region
}
几年后我想补充一点,我完全切换到 JavaScript。
对于我的用例来说,这似乎是更好的语言。
简介
我正在开发一个开源项目 Treez,我在该项目中以树视图组织所谓的 "Atoms"。这些 Atom 有时有 很多属性 并且这些属性可以通过树视图中的用户操作或通过 API一个 Eclipse 代码编辑器。
我的原子本身的属性由可重用"AttributeAtoms"表示。那些保存实际属性值并提供附加功能,如验证("Atom" 的其他可能术语可能是 "widget"、"bean"、"property" 或 "tree node")。
问题
过去我为每个 Atom 属性提供了一个 getter/setter 对。这是很多额外的工作,它会增加我的 Atom classes 的大小(请参阅下面的代码示例)。现在我正在寻找替代解决方案
- 减少创建新 Atom 的工作量(并且减少维护它们的工作量)。
- 避免了 "redundant" 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?
已考虑的选项
一个。使用 IDE
自动生成 getters/settersEclipse 提供了自动生成 getters/setters.
的可能性- 不适用于我的 AttributeAtoms,因为 gettter/setter 代码看起来略有不同。
- 不避免额外的"redundant"代码。
如果我决定保留 getters/setters,我可以尝试为我的 AttributeAtoms 创建类似的东西。另请参阅此 post 关于 JavaFx 属性的(不工作)自动 getter/setter 创建: http://www.eclipse.org/forums/index.php/t/781816/
乙。生成注释 getters/setters (Project Lombok)
Lombok 提供了使用注解自动生成 getters 和 setter 的可能性。
- 对我的 AttributeAtoms 都不起作用
- 我尝试将 Lombok 与 Eclipse 一起使用。编辑器中的代码完成工作有效,但我收到 "method not found" 警告。我可能需要投入更多时间让 Lombok 为 classical 属性工作。
- 另见 Is it safe to use Project Lombok?
如果我决定使用注释来定义 getters/setters,则可以扩展 Lombok 来为我的 AttributeAtoms 工作。
为 JavaFx 属性扩展 Lombok 的请求已存在于此处:https://groups.google.com/forum/#!searchin/project-lombok/getterandsetterforproperties/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 插件,"allows to access private attributes" 通过建议相应的伪造代码完成方法。在编译用户源代码之前,像
这样的假调用myAtom.setMyAttribue(newValue);
将被翻译成真正存在的通用代码 getter(选项 C):
myAtom.set("myAttribute", newValue);
E. Public 属性
如果我将我的 Atom 属性设置为 public,我就不需要在每个 Atom 中添加 getters/setter 代码。相反,可重用的 AttributeAtoms 将提供 get/set 方法。例如,用法看起来像这样
myAtom.myAttribute.get();
myAtom.myAttribute.set(newValue);
而不是
myAtom.getMyAttribute();
myAtom.setMyAttribute(newValue);
一些缺点:
- 用户需要习惯这一点"unconventional approach"。 Java 用户可能期望
setMyAttribute(newValue)
,而 C# 用户可能期望myAtom.myAttribute = newValue
。 可以交换整个 AttributeAtom,我不想允许:
myAtom.myAttribute = completelyDifferentAttribute
有什么改进策略吗?
有没有办法允许访问属性的方法,同时不允许交换属性本身?我需要一个新的访问修饰符,比如
private *publicMethodAccess* AttributeAtom<String> myAttribute;
Atom 代码示例
这里有一个 Atom 的例子 class。如果滚动到底部,您会发现 getters/setters 使用了很多代码行。这很丑,不是吗?
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
}
对于选项 E,通过使用 "final" 修饰符,您可以防止换入一个全新的 AttributeAtom,同时仍然允许 getting/setting:
public final AttributeAtom<String> myAttribute = new FilePathAttributeAtom("myAttribtue");
然后将允许以下内容:
myAtom.myAttribute.get();
myAtom.myAttribute.set(newValue)
但你担心的事情不会是:
myAtom.myAttribute = completelyDifferentAttribute
我最终决定使用一种新模式,它用 "double wrapping":
扩展选项 E- 有一个接口 "Attribute" 提供方法 "T get()" 和 "set(T value)"
- 另一个接口 "AttributeWrapper" 继承自 "Attribute"。它提供了额外的方法 "setAttribute" 和 "getAttribute" 来交换包装的属性。
- AttributeWrapper 的方法 get/set 被重定向到包装的属性。
- 如果 "AttributeWrapper" 作为 "Attribute" 传递给外界,只有方法 "get" 和 "set" 可见。这提供了一些(伪)封装。 (这种封装可能几乎与使用 private 修饰符的封装一样好。无论如何,private 修饰符都可以通过反射绕过。)
- 那些知道我的属性实际上是 AttributeWrappers 的人能够将属性转换为 AttributeWrappers 并交换包装的属性。这也允许我不仅在构造函数中而且在 Atom classes 的任何方法中创建和交换我的 public final 属性。 (将所有属性定义直接放在属性区域或构造函数中会使我的代码难看,因此难以阅读。)
- 有一个 class "Wrap" 提供了 AttributeWrapper 的默认实现。
- 抽象 class AttributeAtom 是我所有属性原子的基础 class。它实现了 Attribute,还提供了一个辅助方法 "wrap"。该方法将 AttributeAtom 包装在传递给该方法的父 AttributeWrapper 中。
最终的工作流程是
- 声明一个 public 最终属性 myAttribute 并立即为其分配一个 Wrap。
- 在初始化方法中创建实际的属性 newAttribute。
- 使用辅助方法"wrap". 将新的Attribute指定为相应Wrap的内容
所有这些都将通过一些示例代码变得更加清晰:
最终使用
MyAtom myAtom = new MyAtom();
String defaultPath = myAtom.myFilePathAttribute.get();
myAtom.myFilePathAttribute.set("D:/newpath.txt")
MyAtomclass 定义的属性和包装的使用
public class MyAtom {
//#region ATTRIBUTES
public final Attribute<String> myFilePathAttribute = new Wrap<>();
//#end region
//...
//#region METHODS
private init(){
//create a new AttributeAtom
//(FilePath inherits from AttributeAtom<String>)
FilePath filePath = new FilePath("C:/defaultpath.txt");
//set the new AttributeAtom as content of the corresponding Wrap myFilePathAttribute
filePath.wrap(myFilePathAttribute);
}
//#end region
}
AttributeAtom
中的辅助方法"wrap"/**
* Wraps this attribute in the AttributeWrapper that is given as Attribute
*
* @param wrap
*/
public void wrap(Attribute<T> wrap) {
Wrap<T> wrapper = (Wrap<T>) wrap;
wrapper.setAttribute(this);
}
接口属性
package org.treez.core.attribute;
/**
* Represents an attribute
*
* @param <T>
*/
public interface Attribute<T> {
/**
* Returns the attribute value
*
* @return
*/
T get();
/**
* Sets the attribute value
*
* @param value
*/
void set(T value);
}
接口属性包装器
package org.treez.core.attribute;
/**
* Wraps a replaceable attribute. The methods are "hidden" if this AttributeWrapper is passed as its parent interface
* Attribute
*
* @param <T>
*/
public interface AttributeWrapper<T> extends Attribute<T> {
/**
* Sets the wrapped Attribute
*
* @param attribute
*/
void setAttribute(Attribute<T> attribute);
/**
* Returns the wrapped Attribute
*
* @return
*/
Attribute<T> getAttribute();
}
Wrap: AttributeWrapper
的实现package org.treez.core.attribute;
import java.util.Objects;
/**
* Default implementation of the AttributeWrapper interface
*/
public class Wrap<T> implements AttributeWrapper<T> {
//#region ATTRIBUTES
private Attribute<T> wrappedAttribute;
//#end region
//#region CONSTRUCTORS
/**
* Constructor
*/
public Wrap() {}
/**
* Constructor with wrapped attribute
*
* @param wrappedAttribute
*/
public Wrap(Attribute<T> wrappedAttribute) {
this.wrappedAttribute = wrappedAttribute;
}
//#end region
//#region ACCESSORS
@Override
public T get() {
Objects.requireNonNull(wrappedAttribute, "Wrapped attribute must be set before calling this method.");
T value = wrappedAttribute.get();
return value;
}
@Override
public void set(T value) {
Objects.requireNonNull(wrappedAttribute, "Wrapped attribute must be set before calling this method.");
wrappedAttribute.set(value);
}
@Override
public Attribute<T> getAttribute() {
return wrappedAttribute;
}
@Override
public void setAttribute(Attribute<T> wrappedAttribute) {
this.wrappedAttribute = wrappedAttribute;
}
//#end region
}
几年后我想补充一点,我完全切换到 JavaScript。 对于我的用例来说,这似乎是更好的语言。