组织Java类以避免使用instanceOf

huangapple go评论78阅读模式
英文:

Structuring java classes to avoid instanceOf

问题

以下是翻译好的部分:

public class ChromaColor {

    private Shader shader;
    private float smoothing;

    public ChromaColor(){
        shader = loadShader("chroma.glsl");
        setSmoothing(0.1F);
    }

    public ChromaColor setSmoothing(float f) {
        if(shader != null) shader.set("smoothing", f);
        this.smoothing = f;
        return this;
    }

    public float getSmoothing() {
        return smoothing;
    }

    public Shader shader() {
        return shader;
    }

}
英文:

I am trying to create an application in java that loads and configures a range of different GLSL shaders. Currently I've got something like the below, but will end up probably with lots more (30+) more similar classes - each loads an individual shader.

Ideally, I'd like to be able to put the shader classes in a list, and link them up to a GUI so that shader parameters can be retrieved and updated. Apart from same common logic (loading the GLSL file and returning a PShader), the variables (and getters/setters) may be quite different for each class (for example, some GLSL shaders I want to use have no variables, while others may have one or multiple variables - float, boolean, int etc).

I could have some kind of base class, and extend the class to handle different variables, but that still probably leads me at some point having to manage some kind of type checking / using a bunch of instanceOf statements. I'm struggling to see how polymorphism could be readily applied here. I've looked into java reflection, but that didn't get me too far, and have been looking at different design patterns.

Note - this isn't homework. It's just a personal project. So, I'm not concerned about having something that has to be maintained by others, but it has piqued my interest in different design patterns, and I'm wondering if there is a better way to structure this.

public class ChromaColor {

    private Shader shader;
    private float smoothing;

    public Chroma(){
        shader = loadShader("chroma.glsl");
        setSmoothing(0.1F);
    }

    public Chroma setSmoothing(float f) {
        if(shader != null) shader.set("smoothing", f);
        this.smoothing = f;
        return this;
    }

    public float getSmoothing() {
        return smoothing;
    }

    public Shader shader() {
        return shader;
    }

}

答案1

得分: 1

除了一些通用逻辑(加载GLSL文件并返回PShader),变量(以及getter/setter)对于每个类可能会有很大的不同...

....

我很难看出多态如何能够轻松地应用在这里。
我已经研究了Java反射,但进展不大,也一直在研究不同的设计模式。

你没有很多选择,因为你不能以统一的方式处理不同类的实例(获取和设置属性)。

要么为每个类定义一个渲染/处理类来处理。
优势:在编译时进行检查。
缺点:需要编写大量重复的代码。

要么使用反射来检索字段以在GUI中进行获取/设置。
优势:需要编写的代码少得多。
缺点:在编译时没有检查。
注意1)使用反射检索所有的getter/setter可能会检索到不希望暴露给GUI的私有字段。
如果是这种情况,注释字段以包含或排除可能是解决这个问题的一种方法。
注意2)如果编写单元测试(可能是参数化测试),检查要处理的不同类的实例的属性是否可以被GUI很好地读取/写入,那么在编译时不进行检查的缺点可以得到缓解。

英文:

> Apart from same common logic (loading the GLSL file and returning a
> PShader), the variables (and getters/setters) may be quite different
> for each class

....

> I'm struggling to see how polymorphism could be readily applied here.
> I've looked into java reflection, but that didn't get me too far, and
> have been looking at different design patterns.

You don't have a lot of ways since you cannot process instances of the different classes (getting and setting properties) in an uniform way.

Either you define a rendering/processing class by class to handle.
Advantage: checks at compile time.
Drawback : much repetitive code to write.

Or you use reflection to retrieve field to get/set in the GUI.
Advantage: much less code to write.
Drawback : no check at compile time.
Note 1) using reflections to retrieve all getters/setters could
retrieve private fields that you don't want to expose to the GUI.
If that is the case, annotate fields to include or to exclude may be a way to address that.
Note 2) the no-check at compile time drawback can be mitigated if you write unit tests (probably parameterized tests) that check that properties of instances of the different classes to handle may well be read/write by the GUI.

答案2

得分: 0

关于是否可以将java.util.Properties的实例作为Shader配置的唯一参数,或者其他类型的java.util.Map,您可以创建一个用于配置的DSL(领域特定语言),以检查每个参数值,并且每种着色器类型在列表中定义了哪些参数对其有效。

这将允许您尽可能多地重用代码。还可以使用getter和setter。可以参考java.time包中的类,了解这个思路的示例...

它可能看起来像这样:

public interface IChromaColors
{
  Set<Parameter> getParameters();
  
  void configure(Map<Parameter, Object> values);
  
  Object get(Parameter parameter);
  
  void set(Parameter parameter, Object value);
}

Parameter可能如下所示:

public interface Parameter<T>
{
  String getName();
  
  Class<T> getType();
  
  boolean validate(T value);
}

现在您的类可能如下所示:

public class ChromaColor implements IChromaColor
{
    private Map<Parameter, Object> m_Values;
    private Set<Parameter> m_ValidValues;
    private Shader shader;
    
    public ChromaColor(Map<Parameter, Object> values)
    {
        shader = loadShader("chroma.glsl");
        m_ValidValues = Set.of(SmoothingParameter);
    
        m_Values = values;
        if (!m_Values.contains(SmoothingParameter)) 
        {
           m_Values.put(SmoothingParameter, Float.valueOf(0.1F));
        }
        configure(m_Values);
    }
    
    public ChromaColor set(Parameter parameter, Object value)
    {
        if (m_ValidValues.contains(parameter))
        {
           
        }
        return this;
    }
    
    public Object get(Parameter parameter)
    {
        return m_Values.get(parameter);
    }
    
    public Shader shader()
    {
        return shader;
    }
}

这只是我想法的初步草稿!所以我省略了Parameter的泛型问题和接口可以进行优化(显著优化!),但我希望您能对我所指的有一点模糊的了解。

英文:

What about using an instance of java.util.Properties as the only parameter for the configuration of a Shader? Or another kind of java.util.Map?

When the shaders all have a simple set of parameters in common (some all of them, some none, others a selection), you can create some type of DSL for the configuration, that checks each parameter value, and each shader type defines in a list which arguments are valid for it.

This would allow you to re-use as much as possible of your code. It would allow also to have getters and setters. Have a look to the classes in java.time for an example how this could look …

It could look like this:

public interface IChromaColors
{
  Set&lt;Parameter&gt; getParameters();

  void configure( Map&lt;Parameter,Object&gt; values );

  Object get( Parameter parameter );

  void set( Parameter parameter, Object value );
}

Parameter may look like this:

public interface Parameter&lt;T&gt;
{
  String getName();

  Class&lt;T&gt; getType();

  boolean validate( T value );
}

Now your class may look like this:

public class ChromaColor implements IChromaColor
{
    private Map&lt;Parameter,Object&gt; m_Values;
    private Set&lt;Parameter&gt; m_ValidValues;
    private Shader shader;

    public Chroma( Map&lt;Parameter,Object&gt; values )
    {
        shader = loadShader(&quot;chroma.glsl&quot;);
        m_ValidValues = Set.of( SmoothingParameter );

        m_Values = values;
        if( !m_Values.contains( SmootingParameter ) ) 
        {
           m_Values.put( SmoothingParameter, Float.valueOf( 0.1F ) );
        }
        configure( m_Values );
    }

    public Chroma set( Parameter parameter, Object value
    {
        if( m_ValidValues.contains( parameter )
        {
           …
        }
        return this;
    }

    public Object get( Parameter parameter )
   {
        return m_Values.get( parameter );
    }

    public Shader shader() {
        return shader;
    }

}

This is just raw, very raw draft of my idea! So I omitted the problems with the generics for Parameter and the interfaces can be optimised (significantly!), but I hope you get a faint hint on what I mean.

huangapple
  • 本文由 发表于 2020年5月2日 16:19:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/61556373.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定