Functional Programming Patterns in Scala: Chatper 2

采用Java编写TinyWeb

Java版的TinyWeb是一个采用传统的面向对象风格编写的基本MVC Web框架.我们使用控制器处理请求,该控制器是使用模板方法模式实现的,采用策略模式实现视图.

核心的数据结构有HttpRequest和HttpResponse,具有不可变性,并且使用生成器模式创建这些对象.

过滤器是在请求被处理之前运行的,他会对请求进行操作.我们使用Filter类是想过滤器.

整个系统概况如下:

系统概况

HttpRequest和HttpResponse

使用生成器模式创建HTTPRequest和httpResponse:

public class HttpResponse{
    private final String body;
    private final Integer responseCode;

    public String getBody() {
        return body;
    }

    public Integer getResponseCode() {
        return responseCode;
    }

    private HttpResponse(Builder builder){
        body = builder.body;
        responseCode = builder.responseCode;
    }

    public static class Builder {
        private String body;
        private String responseCode;

        public Builder body(String body){
            this.body = body;
            return this;
        }

        public Builder responseCode(Integer responseCode){
            this.responseCode = responseCode;
            return this;
        }

        public HttpResponse build() {
            return HttpResponse(this);
        }

        public static Builder newBuilder() {
            return new Bulider();
        }
    }
}

这种方式将全部的可变性封装在一个Builder对象,该对象将会用来创建不可变的HTTPResponse,比如我们创建一个简单的测试请求:

HttpResponse testResponse  = HttpResponse.Builder.newBuilder()
                                .responseCode(200)
                                .body("responseBody")
                                .build();

如果不适用Builder,我们需要将所有需要的参数传递给构造器.

在HttpRequest中,为了支持请求过滤器对传入请求的修改,需要创建一个机遇当前请求对象的新请求,我们将使用buildFrom()来实现,该方法将一个现成的HttpRequest作为入参,并使用它来设置新builder的初始值:

public static Builder builderFrom(HttpRequest request){
    Builder builder  = new Builder();
    builder.path(request.getPaty());
    builder.body(request.getBody());

    Map<String,String> headers = request.getHeaders();
    for (String headerName : headers.keySet())
        builder.addHeader(headerName, headers.get(headerNmae));

    return builder();
}

视图和策略模式

首先我们需要一个view接口,它拥有一个唯一的方法render().render方法以类型Map>的model作为入参,该model代表了模型的属性和值.我们将使用List作为我们值的类型,这样单个属性就可有拥有多个值了.该方法返回一个代表已渲染视图的字符串.

import java.util.List;
import java.util.Map;

public interface View{
    public String render(Map<String, List<String>> model);
}

然后实现两个类,StrategyView和RenderingStrategy,这两个类被设计用来共同实现策略模式.

RenderingStrategy负责完成实际的视图渲染工作,它将由框架的用户负责实现,下面是策略模式中策略类的一个实例:

public interface RenderingStrategy{
    public String renderView(Map<String, List<String>> model);
}

然后是RenderingStrategy的代理类StrategyView,该类由框架实现,并负责对RenderingStrategy抛出的异常进行处理:

public class StrategyView implements View {
    private RenderingStrategy viewRenderer;

    public StrategyView(RenderingStrategy viewRenderer){
        this.viewRenderer = viewRenderer;
    }

    @override
    public String render(Map<String, List<String>> model){
        try {
            return viewRenderer.renderView(model);
        } catch (Exception e){
            throw new RenderingException(e);
        }
    }
}

….