如何扩展防御性编码实现?例如。 vert.xCorsHandlerImpl

How to extends defensively coded implementation? E.g. vert.x CorsHandlerImpl

有像 vert.x 这样的防御性编码库。使用带有静态工厂方式的接口来 return 实现。开发人员可以使用他们的实现,但是当 不完全 需要时 - 无法扩展它。不幸的是,它甚至将实现 绑定到 接口。为什么?

扩展此类 classes 的推荐方法是什么?除了复制整个 class 并只重写需要的几行之外,必须有其他方法...

例如:vert.x io.vertx.ext.web.handler.CorsHandler

的实现
public interface CorsHandler {
  static CorsHandler create(String allowedOriginPattern) {
    return new CorsHandlerImpl(allowedOriginPattern);
  }
  ...
}


public class CorsHandlerImpl implements CorsHandler {
  private boolean isValidOrigin(String origin) {
  }
  ...
}


public class MyCorsHandler implement/extends CorsHandler/impl {
  @Override
  protected boolean isValidOrigin(String origin) {
    // my changes 
  }
}

可以包装 class 而不是扩展它,并且只更改需要不同实现的方法。

此解决方案称为 Adapter Pattern:

In software engineering, the adapter pattern is a software design pattern (also known as Wrapper, an alternative naming shared with the Decorator pattern) that allows the interface of an existing class to be used as another interface.1 It is often used to make existing classes work with others without modifying their source code.

请考虑扩展其他人编写的现有 classes 通常不是一个好主意。事实上,在接下来的版本中,他们的内部实现可能会发生变化,并且这些变化可以反映在扩展 classes 上,以一种有时无法预测的方式改变他们的行为。

这里有一个例子表明:

假设您需要使用自定义实现来扩展 ArrayList 以记录所有添加的项目。

其实你可以重写add和addAll方法如下:

public void add(E e) {
   System.out.println("add item " + e);
   super.add(e);
}

public void addAll(Collection<? extends E> c) {
   for (E e : c) {
       System.out.println("add item " + e);
   }
   super.addAll(c);
}

之所以有效,是因为 addAll 的内部实现不调用 add。但是,如果 addAll 的实现随着对集合 c 的循环而改变,该循环为每个元素调用 add,则您的代码将不起作用。因此,更改基础 class 将更改派生 class.

的行为

如果是 CorsHandler,我会继续扩展 Impl 并覆盖处理方法:

class MyCorsHandler extends CorsHandlerImpl {

    public MyCorsHandler(String allowedOriginPattern) {
        super(allowedOriginPattern);
    }

    @Override
    public void handle(RoutingContext context) {
        HttpServerRequest request = context.request();
        HttpServerResponse response = context.response();
        String origin = context.request().headers().get(ORIGIN);
        if (origin == null) {
            // Not a CORS request - we don't set any headers and just call the next handler
            context.next();
        } else if (isMyValidOrigin(origin)) {
            context.next();
        } else {
            super.handle(context);
        }
    }

    private boolean isMyValidOrigin(String origin) {
        // Do something there
        return false;
    }
}