我一直在关注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或其他方法.
那么为什么在这个精确的情况下使用像这样的供应商?
TLDR
使用a 比显式构造函数调用Supplier带来更好的可读性/可维护性,例如/ statements new MyObject()处理的习语.
它还带来了更好的类型安全性,通过反射进行实例化,这是成语的替代方法,通常在Java 8之前用于解决可维护性问题但引入了其他问题.switchif-else ifnew MyObject()
实例化一个Factory类不一定是说明好处的最好例子Supplier.
所以,假设我们想要Shape从Factory类中实例化类.
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)
此代码在运行时不会因为实例化Square或Circle在编译时检查而失败.
实例化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而不进行工厂更改.
在上面的例子中,与供应商复杂化是没有意义的.一个简单的构造函数调用更容易阅读(并且可能更快).
可能,本考试的作者旨在演示如何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)