是java 8供应商替换构造函数调用

Iri*_*dul 7 java

我一直在关注Java 8的供应商和消费者接口以及我所理解的它可以取代构造函数调用.
我在dzone(链接在这里)上看到了一个使用ShapeFactory的例子.代码非常简单,是一个简单的形状类工厂.
但她如此使用它:

Supplier<ShapeFactory> supplier = ShapeFactory::new;
supplier.get().getShape("rectangle").draw(); 
Run Code Online (Sandbox Code Playgroud)

但为什么它比做一个简单的经典更好:

ShapeFactory factory = new ShapeFactory();
factory.getShape("rectangle").draw()
Run Code Online (Sandbox Code Playgroud)

这足够简单和有效.此外,如果ShapeFactory类的构造函数具有参数,则Supplier将无法工作,我们将不得不使用Function或其他方法.

那么为什么在这个精确的情况下使用像这样的供应商?

dav*_*xxx 7

TLDR

使用a 比显式构造函数调用Supplier带来更好的可读性/可维护性,例如/ statements new MyObject()处理的习语. 它还带来了更好的类型安全性,通过反射进行实例化,这是成语的替代方法,通常在Java 8之前用于解决可维护性问题但引入了其他问题.switchif-else if
new MyObject()


实例化一个Factory类不一定是说明好处的最好例子Supplier.
所以,假设我们想要ShapeFactory类中实例化类.

Shape应该实例化不同类型的工厂Shape(它的子类).

1)如果没有Supplier和使用new MyShape()成语,您将使用一个方法来完成,该方法包含一些if-else if/ switch语句,用于检查一种条件/参数,并根据此条件/参数立即预测类.
例如 :

public static Shape createShape(String criteria) {
    if (criteria.equals("circle")){
        return new Circle();
    }
    else if (criteria.equals("square")){
        return new Square();
    }
    ...
}  
Run Code Online (Sandbox Code Playgroud)

这很糟糕,因为当您通过该方法添加要处理的类时,您必须使用new if-else if/ 更改此方法,switch以便将其考虑在内.
它导致无法维护的代码,您可以快速创建副作用.

2)为了避免这个问题,我们经常使用反射Class.newInstance().它消除了if-else if/ switch问题,但它经常创建其他因为反射可能不起作用(安全问题,类不可实例化等等),并且您将仅在运行时知道它.
它仍然导致脆弱的代码.

以下是赞成的理由Supplier:

通过提供a Supplier,在编译时执行检查 :如果类不可实例化,则编译器将发出错误.
此外,使用/接受a的方法Supplier<Shape>不需要使用 if-else if/ switchstatements.

使用时Supplier,通常会遇到两种情况(当然不是互斥的):

  • Supplier<Shape>对象由工厂类实例化.
    在工厂,我们可以因此,例如使用Map存储的Supplier<Shape>实例和 修改代码以在地图上添加/删除元素,只需添加一个新的分支,以一个真正的清洁剂if-else if/ switch声明,因为它是冗长而改变的方式要少得多map populating(添加map.put()或删除map.put()语句)不太容易产生副作用.

  • Supplier<Shape>对象被实例化,并且由客户端类提供.在这种情况下,工厂甚至不需要改变.
    所以地图甚至不是必需的.
    从客户端来看,虽然客户端提供了一个有效的 Supplier<Shape>参数,但它很好.

键入安全性,可维护代码:正如您可能注意到的那样,这两种使用Supplier<Shape>地址的方式完全弊端new MyShape()和反射习惯用法实例化.


我将举两个例子来说明这两种方式.


Shape Suppliers在工厂中创建的示例:

public class SimpleShapeFactory {


    private static Map<String, Supplier<Shape>> shapesByCriteria = new HashMap<>();

    static {
        shapesByCriteria.put("square", Square::new);
        shapesByCriteria.put("circle", Circle::new);
    }

    public static Shape createShape(String criteria) {
        return shapesByCriteria.get(criteria).get();
    }           

}
Run Code Online (Sandbox Code Playgroud)

客户端可以通过以下方式调用它:

Shape square = SimpleShapeFactory.createShape("square");
Shape circle = SimpleShapeFactory.createShape("circle");
Run Code Online (Sandbox Code Playgroud)

此代码在运行时不会因为实例化SquareCircle在编译时检查而失败.
实例化Shape实例的任务位于同一个地方且易于更改:

static {
    shapesByCriteria.put("square", Square::new);
    shapesByCriteria.put("circle", Circle::new);
}
Run Code Online (Sandbox Code Playgroud)

Shape Suppliers由客户提供的示例:

public class ComplexShapeFactory {

    public static Shape composeComplexShape(List<Supplier<Shape>> suppliers) {
        Shape shape = suppliers.get(0);
        for (int i = 1; i < suppliers.size() - 1; i++) {
            shape = shape.compose(suppliers.get(i + 1).get());
        }

        return shape;
    }    

}
Run Code Online (Sandbox Code Playgroud)

客户端可以通过Supplier以这种方式链接来创建复杂的形状,因为它调用方法:

Shape squareWithTwoCircles = ComplexShapeFactory.composeComplexShape(Arrays.asList(Square::new, Circle::new, Circle::new));
Run Code Online (Sandbox Code Playgroud)

检查仍在编译时完成,并且由于供应商由客户提供,客户可以添加新类Shape而不进行工厂更改.


Ale*_*rov 6

在上面的例子中,与供应商复杂化是没有意义的.一个简单的构造函数调用更容易阅读(并且可能更快). 可能,本考试的作者旨在演示如何Supplier调用.

当您需要提取对象的检索(不一定是创建)时,供应商会变得有用,这种"策略模式".例如:

public void drawRectangle(final Supplier<ShapeFactory> factorySupplier) {
    final ShapeFactory factory = factorySupplier.get();
    factory.getShape("rectangle").draw();
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以像这样调用您的方法,它将创建一个新工厂:

drawRectangle(ShapeFactory::new); 
Run Code Online (Sandbox Code Playgroud)

或者像这样:

@Autowired
private ShapeFactory shapeFactoryBean;
...
drawRectangle(() -> shapeFactoryBean); 
Run Code Online (Sandbox Code Playgroud)

它将使用现有工厂而不是创建新工厂.


当然,我的例子中的方法可以只ShapeFactory作为一个参数.但是可以想象一种情况,例如,工厂只需要在某些情况下访问,因此不需要为方法调用创建新工厂,例如:

public void drawRectangleConditionally(final Supplier<ShapeFactory> factorySupplier) {
    if (something) {
        final ShapeFactory factory = factorySupplier.get();
        factory.getShape("rectangle").draw();
    }
}
...
drawRectangleConditionally(ShapeFactory::new); 
// the factory will only be created if it's really needed. 
Run Code Online (Sandbox Code Playgroud)

请注意,供应商可以多次使用,因此供应商的另一个用途是获取一系列对象,例如:

public List<T> createList(final int n, final Supplier<T> listItemSupplier) {
    final List<T> result = new ArrayList<>(n);
    for (int i = 0; i < n; ++i) {
        result.add(listItemSupplier.get());
    }
    return result;
}
...
createList(5, MyObject::new); // creates a list of 5 MyObjects
createList(3, () -> "hello"); // creates a list of 3 "hello" strings
createList(10, Random::nextLong); // creates a list of 10 random longs
Run Code Online (Sandbox Code Playgroud)