Generelize un tipo a un sistema de tipos en Java

ılya.k. :

Tengo algo de código donde no es objeto, como eventos , que tiene una colección de atributos , que tiene valores que cualquier persona puede obtener mediante una llamada al método Evento : Doble getAttributeValue (índice entero) . Hasta ahora AtributeValue como en la firma del método era siempre doble. Ahora puede haber otras clases nuevas y necesito generalizar el código para todos los lugares donde los operadores aplicados en doble deben trabajar en mi nuevo sistema de clases. Por otra parte las clases deben funcionar no sólo como en el viejo código, uno a una misma clase, sino que también interactúan con los mismos operadores entre uno a otro. por ejemplo, los grupos de edad el código operado de esta manera:

private Double calculateDelta(Event event) {
        Double firstValue = (Double)event.getAttributeValue(StockEventTypesManager.firstStockMeasurementIndex);
        Double secondValue = (Double)event.getAttributeValue(StockEventTypesManager.firstStockMeasurementIndex + 1);
        return Math.abs(firstValue - secondValue);
    }  

El nuevo código debe funcionar algo como esto:

private AttributeValue calculateDelta(Event event) {
        AttributeValue firstValue = (AttributeValue )event.getAttributeValue(StockEventTypesManager.firstStockMeasurementIndex);
        AttributeValue secondValue = (AttributeValue )event.getAttributeValue(StockEventTypesManager.firstStockMeasurementIndex + 1);
        return Math.abs(AttrValueFunctional.minus(firstValue,secondValue));
    }

o algo así (sólo esas sugerencias, porque no sé cómo diseñarlo):

private AttributeValue calculateDelta(Event event) {
            AttributeValue firstValue = (AttributeValue )event.getAttributeValue(StockEventTypesManager.firstStockMeasurementIndex);
            AttributeValue secondValue = (AttributeValue )event.getAttributeValue(StockEventTypesManager.firstStockMeasurementIndex + 1);
            return (firstValue.minus(secondValue)).abs();
    }

donde el código puede ser correspondiente a cada uno de esos pares de afirmaciones:

boolean isDoubleWrapper = firstValue isinstanceof DoubleWrapper; //true
boolean isDoubleList = secondValue isinstanceof DoublePairsList; //true

boolean isDoubleList = firstValue isinstanceof DoublePairsList; //true
boolean isDoubleWrapper = secondValue isinstanceof DoubleWrapper; //true

boolean isDoubleWrapper = firstValue isinstanceof DoubleWrapper; //true
boolean isDoubleWrapper = secondValue isinstanceof DoubleWrapper; //true

boolean isDoublePairsList = firstValue isinstanceof DoublePairsList; //true
boolean isDoublePairsList = secondValue isinstanceof DoublePairsList; //true

también podría ser:

boolean isStrangeObject = firstValue isinstanceof StrangeObject; //true
boolean isDoubleList = secondValue isinstanceof DoublePairsList; //true

boolean isDoubleList = firstValue isinstanceof DoublePairsList; //true
boolean isStrangeObject = secondValue isinstanceof StrangeObject; //true

boolean isStrangeObject = firstValue isinstanceof StrangeObject; //true
boolean isStrangeObject = secondValue isinstanceof StrangeObject; //true

boolean isDoublePairsList = firstValue isinstanceof DoublePairsList; //true
boolean isDoublePairsList = secondValue isinstanceof DoublePairsList; //true

Mi gran objetivo del diseño es hacer posible que cualquier programador en un futuro añadir fácilmente nuevas clases que podría "ocultar" en AttributeValue, añadir la funcionalidad operativa necesaria (sumar, restar, ABS), por lo que no debe haber una buena escalabilidad a nuevas clases .

Probé algunas implementaciones, una de ellas funciona en general (el que mostró más abajo), pero yo no creo que está siguiendo los mejores patrones de diseño Java o tiene buena escalabilidad y fácil de añadir nuevas clases culo que he mencionado.

public class Main {
    public interface Operand {}

    public interface Combiner<T> {
        T subtract(T a, T b);
    }

    public static abstract class MyFunctional {

        public static<T extends Operand> T
        compute(Operand a, Operand b, Subtracter combiner) {
            return (T) combiner.subtract(a, b);
        }

        private static A subtractAA(A a, A b){
            return new A(a.getFirst() - b.getFirst(), a.getSecond()-b.getSecond());
        }

        private static A subtractAB(A a, B b){
            return new A(a.getFirst() - b.getFirst(), a.getSecond()-b.getSecond()-b.getThird());
        }

        static class
        Subtracter implements Combiner<Operand> {
            @Override
            public Operand subtract(Operand x, Operand y) {

                if(x instanceof A && y instanceof A){
                    return subtractAA((A)x,(A)y);
                }
                if(x instanceof A && y instanceof B){
                    return subtractAB((A)x,(B)y);
                }
                return null;
            }
        }

    }


    public static class A implements Operand{
        private int a;
        private int b;

        public A(int a, int b){
            this.a=a;
            this.b=b;
        }
        public int getFirst(){
            return a;
        }

        public int getSecond() {
            return b;
        }

        @Override
        public String toString() {
            return "("+a+" "+b+")";
        }
    }

    public static class B implements Operand {
        private int a;
        private int b;
        private int c;

        public B(int a, int b, int c){
            this.a=a;
            this.b=b;
            this.c = c;
        }
        public int getFirst(){
            return a;
        }

        public int getSecond() {
            return b;
        }

        public int getThird() {
            return b;
        }

        @Override
        public String toString() {
            return "("+a+" "+b+" "+c+")";
        }
    }

    public static void main(String[] args) {

        Operand e = new A(1, 2);
        Operand f = new A(3,4);
        Operand g = new B(3,4,5);
        System.out.println("Hello World");

        System.out.println((Object)MyFunctional.compute(e,f, new MyFunctional.Subtracter()));

        Operand o = MyFunctional.compute(f,g, new MyFunctional.Subtracter());

        System.out.println("Bye");
    }
}

Eso es una especie de un pequeño ejemplo de lo que he intentado hacer. ¿Alguien puede tratar de cambiar mi código o sugerir su propio código para aquellos simples A, clases B, que serán fáciles de adaptarse a las nuevas clases (por ejemplo, si se quiere añadir un poco sencilla de clase C con 4 campos y las operaciones de suma y resta entre C y en sí y entre a y C y B y C), que funcionará como he descrito aquí y en el que pide (o menos lo mismo en realidad).

Redactar después de unas respuestas: El verdadero dominio de los nuevos tipos: Así que en el viejo problema, el tipo Double significó un valor determinista (los medios que este valor es conocido en el mundo real con una probabilidad de 1,0). Ahora tengo que sustituirlo por el sistema de tipos en la imagen. introducir descripción de la imagen aquíAhora acerca de la operación y funcionalidad operandos: Cada dos objetos en la misma rama debe ser operado como primer antepasado con él mismo. Dos objetos de diferentes ramas deben actuar como sus antepasados primeros actos con misma operación.

Así que, finalmente, quiero refactorizar el código para el nuevo supuesto de que:

event.getAttributeValue(index);

can return any of the classes in the described possible hierarchy. Of course right now I need to write a code that will implement only the two left most high objects and all the operations between them, as subtract, add, and so on. And my goal is to design the correct skeleton of interfaces and classes for this problem.

Another clarification due to the last updated answer:

what I want to do is some kind of wrapper and Functional as Strategy design pattern, something as in the two codes in the next links:

first (not templated example which I need to generalize according to the last updated answer): first

second (something like this, but using Class, Class and both BiFunction, and BinaryOperator according to the suggested solution): second

VGR :

There is already a class for this: BinaryOperator.

Forget about a wrapper class like AttributeValue. Just let the Event continue to hold simple values like it did before, including Doubles, and pass in the combining operation as a BinaryOperator.

Casting to a generic type (like T) is an unsafe operation due to type erasure, so you will want to pass the type as well:

private <V> V calculateDelta(Event event,
                             Class<V> valueType,
                             BinaryOperator<V> operation) {

    V firstValue = valueType.cast(
        event.getAttributeValue(
            StockEventTypesManager.firstStockMeasurementIndex));

    V secondValue = valueType.cast(
        event.getAttributeValue(
            StockEventTypesManager.firstStockMeasurementIndex + 1));

    return operation.apply(firstValue, secondValue);
}

A call to that method would look like this:

Double delta =
    calculateDelta(eventToProcess, Double.class, (a, b) -> a - b);

Update:

You point out in a comment that the operands aren’t always of the same type. In that case, you would use BiFunction instead of BinaryOperator:

private <V, U, R> R calculateResult(Event event,
                                    Class<V> firstValueType,
                                    Class<U> secondValueType,
                                    BiFunction<V, U, R> operation) {

    V firstValue = firstValueType.cast(
        event.getAttributeValue(
            StockEventTypesManager.firstStockMeasurementIndex));

    U secondValue = secondValueType.cast(
        event.getAttributeValue(
            StockEventTypesManager.firstStockMeasurementIndex + 1));

    return operation.apply(firstValue, secondValue);
}

However, it turns out you can do both! You can have the above method, and also the first version as a convenience, for cases where you expect both values, and the result, to be the same type:

private <V> V calculateResult(Event event,
                              Class<V> valueType,
                              BinaryOperator<V> operation) {

    return calculateResult(event, valueType, valueType, operation);
}

aclaraciones:

BiFunction y BinaryOperator son cada funcionales de interfaces, es decir, una interfaz con exactamente un método abstracto. ( staticY defaultmétodos no son métodos abstractos.)

Esto significa una lambda puede ser utilizado para representarla. (a, b) -> a - b)es idéntica a ésta:

new BinaryOperator<Double>() {
    @Override
    public Double apply(Double a, Double b) {
        return a - b;
    }
}

Por lo tanto, la lambda es de hecho el BiFunction o BinaryOperator.

No tiene que ser un lambda. También puede ser una referencia método:

private Double subtract(Double a, Double b) {
    return a - b;
}

// ...

Double delta =
    calculateResult(eventToProcess, Double.class, this::subtract);

Puede, por supuesto, hacer que un método más complejo como:

private Double probabilityFor(DiscreteDistribution d, Integer x) {
    Map<Integer, Double> probabilities = d.getProbabilities();
    return probabilities.getOrDefault(x, 0);
}

// ...

Double probability =
    calculateResult(eventToProcess,
                    DiscreteDistribution.class,
                    Integer.class,
                    this::probabilityFor);

También se pueden definir operaciones sobre los mismos objetos complejos:

public class DiscreteDistribution {
    private final Map<Integer, Double> probabilities = new HashMap<>();

    // ...

    public Double probabilityOf(int x) {
        return probabilities.getOrDefault(x, 0);
    }
}

public class ValueCalculator {

    // ...

    Double probability =
        calculateResult(eventToProcess,
                        DiscreteDistribution.class,
                        Integer.class,
                        DiscreteDistribution::probabilityOf);

}

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=364337&siteId=1
Recomendado
Clasificación