“Special attributes/properties” instead of getter/setter in Java to avoid boiler plate code

牧云@^-^@ 提交于 2019-12-06 16:03:15

For Option E, by using the "final" modifier you could prevent a whole new AttributeAtom being swapped in, whilst still allowing getting/setting:

public final AttributeAtom<String> myAttribute = new FilePathAttributeAtom("myAttribtue");

Then the following will be allowed:

myAtom.myAttribute.get();
myAtom.myAttribute.set(newValue)

But the thing you're worried about won't be:

myAtom.myAttribute = completelyDifferentAttribute

I finally decided to use a new pattern which extends option E with a "double wrapping":

  • There is an interface "Attribute" which provides the methods "T get()" and "set(T value)"
  • Another interface "AttributeWrapper" inherits from "Attribute". It provides the additional methods "setAttribute" and "getAttribute" to exchange a wrapped Attribute.
  • The methods get/set of the AttributeWrapper are redirected to the wrapped Attribute.
  • If an "AttributeWrapper" is passed as "Attribute" to the outside world, only the methods "get" and "set" are visible. This provides some (pseudo) encapsulation. (That encapsulation might be nearly as good as the encapsulation with a private modifier. The private modifier can be bypassed with reflection anyway.)
  • Those who know the fact that my Attributes are actually AttributeWrappers are able to cast the Attributes to AttributeWrappers and exchange the wrapped Attributes. This also allows me to create and exchange my public final Attributes not only in the constructor but also in any method of my Atom classes. (Putting all attribute definitions directly in the attribute region or in the constructor would make my code ugly and therefore hard to read.)
  • There is a class "Wrap" which provides a default implementation of the AttributeWrapper.
  • The abstract class AttributeAtom is the base class for all my attribute atoms. It implements Attribute and also provides a helper method "wrap". That method wraps the AttributeAtom in a parent AttributeWrapper that is passed to the method.
  • The final work flow is

    1. Declare a public final Attribute myAttribute and immediately assign a Wrap to it.
    2. Create the actual Attribute newAttribute in an initialization method.
    3. Assign the new Attribute as content of the corresponding Wrap by using the helper method "wrap".
  • All this is going to be more clear with some example code:

Final Usage

MyAtom myAtom = new MyAtom();
String defaultPath = myAtom.myFilePathAttribute.get();
myAtom.myFilePathAttribute.set("D:/newpath.txt")

Usage of Attribute and Wrap for the class definition of MyAtom

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
}

Helper method "wrap" in AttributeAtom

/**
 * 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);
}

Interface Attribute

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);

}

Interface AttributeWrapper

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: The implementation of 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

}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!