摘 要:从Java的传统设计模式出发,对比Scala的面向函数编程实现。
关键词:设计模式;Java;Scala
0 行为模式
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。
行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。
1 策略模式和模板模式
1.1 策略模式,顾名思义就是类的行为上的策略,或是不同算法的使用策略。大体的理解就是随着情况的变化来选择不同的策略,对比于硬编码(多重条件转移语句)的分支实现,策略模式符合开闭原则。
策略模式主要由三部组成:策略接口(Strategy),具体策略(Concrete Strategy),策略环境(Context)。如图所示。
1.2 模板方法模式,指事先给出了一个功能的流程,具体流程中的逻辑,由继承的子类实现。
2 小型MVC框架
下面将会用一个MVC的小型Web架构,其中的View部分可以用策略模式实现。架构如图。
3 Java的代码实现
3.1 策略模式
/**
* 用于生成响应体(HttpResponse.body)的接口
*/
public interface View {
String render(Map<String, List<String>> model);
}
/**
* 策略接口
*/
public interface RenderingStrategy {
public String renderView(Map<String, List<String>> model);
}
/**
* 策略模式下的Context类,用于组合不同的策略
*/
public class StrategyView implements View {
/**
* 策略提供的实例
*/
private RenderingStrategy renderingStrategy;
public StrategyView(RenderingStrategy renderingStrategy){
this.renderingStrategy=renderingStrategy;
}
/**
* 使用策略来渲染View
*
* @param model
* @return
*/
@Override
public String render(Map<String, List<String>> model) {
return renderingStrategy.renderView(model);
}
}
我们只需要实现RenderingStrategy接口,然后,用不同的实现类去实例化StrategyView类就可以使用不同的策略,当然也可以在StrategyView中给出调整策略的方法,这样将增加代码的灵活性。
4 Scala代码的实现
4.1 策略模式
View保持不变代码如下:
trait View {
def render(model:Map[String,List[String]]):String
}
Strategy做如下的改变:
/**
* 用高阶函数来实现函数式接口
* renderingStrategy现在是一个函数变量,用var修饰时,会生成get和set方法,方便动态修改策略。
* 这样,对比于面向对象的接口实现策略,函数式策略更加简单。
* @param renderingStrategy
*/
class StrategyView(var renderingStrategy: Map[String,List[String]] => String) extends View {
override def render(model: Map[String, List[String]]): String = {
renderingStrategy(model)
}
}
这相当于对函数式接口的改变,策略接口是对象的行为,也就是单个或多个函数的组合,这样就可以通过一个函数变量来实现策略接口。
4.2 Scala模板方法模式实现Controller
/**
* 控制器接口
* 包含了对HttpRequest的基本处理逻辑
*/
trait Controller {
/**
* 处理request,返回response
* @param request
* @return
*/
def handleRequest(request:HttpRequest):HttpResponse
}
/**
* 模板类,用于代理特定的任务
*
* java实现模板模式,doRequest需要子类来继承,进而实现不同的控制器
*
* scala实现模板模式,用一个函数变量doRequest即可完成,不同的控制器可以由传入的不同函数来实现。
*
* 注意:模板方法模式采用继承来完成工作--类的行为模式,策略方法模式采用组合来完成工作--对象的行为模式
*/
class TemplateController(view: View, doRequest: HttpRequest => Map[String, List[String]]) extends Controller {
/**
* 处理request,返回response
*
* @param request
* @return
*/
override def handleRequest(request: HttpRequest): HttpResponse = {
val resBuilder = HttpResponse.Builder.newBuilder()
try {
val model = doRequest(request)
val body = view.render(model)
resBuilder.body(body)
}catch {
case e:Exception => resBuilder.responseCode(0)
}
resBuilder.build()
}
}
5 scala组合Web框架
TinyWeb只需要传入一组控制器和一组过滤器就可以指定自己的Web应用了,
/**
* 框架的管线装置
* 将所有组件组合在一起
*/
class TinyWeb(controllers: Map[String, Controller], filters: List[HttpRequest => HttpRequest]) {
/**
* 处理request
*
* @param request
* @return
*/
def handleRequest(request:HttpRequest): Option[HttpResponse] = {
//1.过滤request
// filters.foreach(filter => currentRequest = filter(currentRequest))
//a compose b == a(b()),组合同类型的函数,结果就是嵌套组合了filter函数
val composeFilter = filters.reverse.reduceLeft((current,next) => current.compose(next))
val newRequest = composeFilter(request)
//2.处理request
//每个path对应一个controller
//get返回Option是一个controller集合,调用集合中过的controller.handleRequest将返回Option[Response]
val controllerOption = controllers.get(newRequest.getPath())
controllerOption.map{
controller => controller.handleRequest(newRequest)
}
}
}
6 使用Web框架
import tinywbe.HttpRequest
import tinywbe.framework_scala.view.StrategyView
object ExampleScala {
def main(args: Array[String]): Unit = {
val greetingController = new TemplateController(new StrategyView(greetingViewStrategy), doRequest)
val controllers = Map("greetings" -> greetingController)
val filters = List({ x: HttpRequest => println("filter" + 1); x }, { x: HttpRequest => println("filter" + 2); x }, { x: HttpRequest => println("filter" + 3); x })
new TinyWeb(controllers, filters).handleRequest(
HttpRequest.Builder.newBuilder()
.body("12,12,34,56,ff,gg")
.path("greetings")
.build()
).foreach(x => println(x.getBody))
}
//定义控制器与模型层交互的函数
def doRequest(request: HttpRequest): Map[String, List[String]] = {
val path = request.getPath
val data1 = request.getBody
val data2 = data1.split(",").toList.map(name => s"${name} --handle")
Map(path -> data2)
}
//定义View渲染的函数
def greetingViewStrategy(model: Map[String, List[String]]) = {
val sb = new StringBuilder
sb.append("<h1> greeting: </h1>\n")
model("greetings").foreach(line => sb.append(s"<h2> $line </h2>\n"))
sb.toString
}
}
6 结束语
简单来看,函数式编程语言对于行为模式的实现有天生的优势,不需要去维护函数式接口,这样会极大的减少项目的代码量。
可以总结,对于面向对象语言中的函数式接口(例如,常用的回调接口),若引入函数式编程的思想将更加简洁。