Введение
Я работаю над проектом с открытым исходным кодом Treez, в котором я организовываю так называемые «атомы» в виде дерева. . Эти атомы иногда имеют множество атрибутов, и эти атрибуты изменяются либо с помощью действий пользователя в древовидном представлении, либо с помощью API в редакторе кода Eclipse.
Сами атрибуты моих атомов представлены повторно используемыми «AttributeAtoms». Они содержат фактическое значение атрибута и предоставляют дополнительные функции, такие как проверка (другие возможные термины для «Atom» могут быть «виджет», «компонент», «свойство» или «узел дерева»).
Вопрос (ы)
Раньше я предоставлял пару геттер / сеттер для каждого из моих атрибутов Atom. Это большая дополнительная работа, и это увеличивает размер моих классов Atom (см. Примеры кода ниже). Сейчас я ищу альтернативное решение, которое
- делает меньше работы по созданию новых атомов (и меньше работы по их обслуживанию).
- позволяет избежать "избыточного" кода котельной плиты геттера / сеттера.
Ниже я опишу несколько вариантов. Какой из вариантов вы бы использовали? Есть ли у вас предложения, как улучшить эти параметры? Вы знаете еще другие варианты?
Пример кода геттера / сеттера
private AttributeAtom<String> myAttribute = new FilePathAttributeAtom("myAttribtue");
public String getMyAttribute() {
return myAttribute.getValue();
}
public void setMyAttribute(String value) {
this.myAtrribute.setValue(value);
}
Похожие статьи
- есть ли в Java что-то похожее на свойства C #?
- (нет) свойств в Java?
- http://blog.netopyr.com/2011/05/19/creating-javafx-properties/
- http://www.eclipse.org/forums/index.php/t/781816/
- Зачем использовать геттеры и сеттеры?
- В чем преимущество наличия частный атрибут с геттерами и сеттерами?
Рассмотренные варианты
А. Автоматически сгенерированные геттеры / сеттеры с IDE
Eclipse предоставляет возможность автоматически генерировать геттеры / сеттеры.
- Не работает для моих AttributeAtoms, поскольку код получателя / установщика выглядит немного иначе.
- Не избегает лишнего «избыточного» кода.
Если я решу сохранить геттеры / сеттеры, я могу попытаться создать что-то подобное для своих AttributeAtoms. Также см. Этот пост о (не работающем) автоматическом создании получателя / установщика для свойств JavaFx: http://www.eclipse.org/forums/index.php/t/781816/
Б. Аннотации для генерации геттеров / сеттеров (Project Lombok)
Lombok предоставляет возможность использовать аннотации для автоматического создания геттеров и сеттеров.
- Не работает для моих AttributeAtoms
- Я пробовал использовать Lombok с Eclipse. Автозавершение кода в редакторе работало, но я получал предупреждения «метод не найден». Возможно, мне придется потратить еще немного времени, чтобы заставить Ломбока работать с классическими атрибутами.
- Также см. Безопасно ли использовать Project Lombok?
Если я решу использовать аннотации для определения геттеров / сеттеров, возможно, удастся расширить Lombok для работы с моими AttributeAtoms.
Запрос на расширение Lombok для свойств JavaFx уже существует здесь: https://groups.google.com/forum/#!searchin/project-lombok/getter$20and$20setter$20for$20properties/project-lombok/Ik6phpmxDXD / а>
Вот введение о том, как расширить Lambok с помощью пользовательских преобразований: http://notatube.blogspot.de/2010/12/project-lombok-creating-custom.html
С. Один универсальный метод получения / установки для всех атрибутов
Я мог бы использовать одну пару геттер / сеттер для всех атрибутов Atom, таких как
Object get(String attributeName)
void set(String attriuteName, Object value)
- Безопасность типов можно улучшить, передав дополнительные аргументы типа.
- Однако завершение кода для моего Atom будет предлагать только один метод получения / установки, и пользователь не увидит, какие атрибуты доступны. (Возможно, это можно было бы решить, используя перечисления вместо строк для идентификации атрибутов. Но эти перечисления необходимо каким-то образом создать. Также см. Следующий вариант.)
Д. Пользовательский редактор Eclipse и обработка кода
Возможно, я мог бы написать дополнительный плагин Eclipse для моего проекта с открытым исходным кодом, который «разрешает доступ к частным атрибутам», предлагая соответствующие поддельные методы для завершения кода. Перед компиляцией исходного кода пользователя необходимо выполнить ложные вызовы типа
myAtom.setMyAttribue(newValue);
будет переведен в код реально существующего обобщенного геттера (вариант C):
myAtom.set("myAttribute", newValue);
Э. Общедоступные атрибуты
Если я сделаю свои атрибуты Atom общедоступными, мне не понадобится код геттеров / сеттеров в каждом Atom. Вместо этого повторно используемые AttributeAtoms предоставят методы get / set. Например, использование могло бы выглядеть так
myAtom.myAttribute.get();
myAtom.myAttribute.set(newValue);
вместо
myAtom.getMyAttribute();
myAtom.setMyAttribute(newValue);
Некоторые недостатки:
- Пользователям необходимо привыкнуть к такому «нестандартному подходу». Пользователи Java могут ожидать
setMyAttribute(newValue)
, а пользователи C # могут ожидатьmyAtom.myAttribute = newValue
. Можно обменять весь AttributeAtom, что я не хочу разрешать:
myAtom.myAttribute = completelyDifferentAttribute
Какие-нибудь стратегии по улучшению этого?
Есть ли способ разрешить доступ к методам атрибута, не позволяя обмениваться самим атрибутом? Мне понадобится новый модификатор доступа, например
private *publicMethodAccess* AttributeAtom<String> myAttribute;
Пример кода Atom
Вот пример класса Atom. Если вы прокрутите вниз, вы найдете много строк кода, которые используются геттерами / сеттерами. Это уродливо, правда?
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
}