首先,通常你不能使用语言的某种机制来实现设计模式,因为设计模式不能直接用语言类型系统和语法来表达。设计模式描述了解决软件开发中反复出现的问题的特定方法。一旦语言提供了一种直接表达设计模式的机制,这不再被认为是设计模式。因此,在一种语言中是一种设计模式的东西,在另一种语言中变成了一种机制。例如,汇编语言中的循环是一种设计模式,尽管在大多数现代语言中它是一种句法结构。设计模式的存在通常表明缺乏特定语言或编程范式的表现力。虽然,无论你的语言多么富有表现力,总有抽象,那可以
您还应该了解,GoF 设计模式是在考虑 OOP 范式的情况下编写的,并考虑了当时 OOP 语言的特性和局限性。因此,在 OCaml/Reason 或任何其他具有参数多态性和一流函数的语言中,它们并不总是适用甚至不需要。
特别是,Builder 模式试图解决的问题是缺少一流的构造函数和参数多态性。由于我们在 Reason 中两者都有,我们通常不会为设计复杂的类型层次结构而烦恼。OOP 的另一个限制是缺少代数数据类型,这是实现复杂复合数据结构(例如抽象语法树(表达式解析树)等)的理想语言机制。
尽管如此,您仍然可以在 Reason 中使用 Builder 模式,但很可能您实际上并不需要它,因为该语言提供了更好、更易表达的机制来解决您的问题。让我们使用来自 Wikipedia 的SportsCarBuilder代码作为我们的工作示例,
/// <summary>
/// Represents a product created by the builder
/// </summary>
public class Car
{
public string Make { get; }
public string Model { get; }
public int NumDoors { get; }
public string Colour { get; }
public Car(string make, string model, string colour, int numDoors)
{
Make = make;
Model = model;
Colour = colour;
NumDoors = numDoors;
}
}
/// <summary>
/// The builder abstraction
/// </summary>
public interface ICarBuilder
{
string Colour { get; set; }
int NumDoors { get; set; }
Car GetResult();
}
/// <summary>
/// Concrete builder implementation
/// </summary>
public class FerrariBuilder : ICarBuilder
{
public string Colour { get; set; }
public int NumDoors { get; set; }
public Car GetResult()
{
return NumDoors == 2 ? new Car("Ferrari", "488 Spider", Colour, NumDoors) : null;
}
}
/// <summary>
/// The director
/// </summary>
public class SportsCarBuildDirector
{
private ICarBuilder _builder;
public SportsCarBuildDirector(ICarBuilder builder)
{
_builder = builder;
}
public void Construct()
{
_builder.Colour = "Red";
_builder.NumDoors = 2;
}
}
public class Client
{
public void DoSomethingWithCars()
{
var builder = new FerrariBuilder();
var director = new SportsCarBuildDirector(builder);
director.Construct();
Car myRaceCar = builder.GetResult();
}
}
我们将提供从 C# 到 Reason 的一对一翻译,以展示 Reason 中 C# 机制的直接对应物。请注意,我们不会构建惯用的 Reason 代码,人们不太可能遵循 Reason 中的 Builder 模式。
该类Car
定义了构建产品的接口。我们将在 Reason 中将其表示为模块类型:
module type Car = {
type t;
let make : string;
let model : string;
let numDoors : int;
let colour: string;
let create : (~make:string, ~model:string, ~numDoors:int, ~colour:string) => t;
};
我们决定将汽车类型抽象化(让实现者选择特定的实现,无论是记录、对象还是汽车 SQL 数据库的键。
我们现在将为汽车制造商定义一个相应的接口:
module type CarBuilder = {
type t;
type car;
let setColour : (t,string) => unit;
let getColour : t => string;
let setNumDoors : (t,int) => unit;
let getNumDoors : t => int;
let getResult : t => car;
}
现在让我们实现一个具体的构建器。由于我们决定将汽车类型抽象化,因此我们需要使用汽车类型参数化我们的具体构建器。在 OCaml/Reason 中,当您需要使用类型参数化某些东西时,通常使用函子。
module FerariBuilder = (Car: Car) => {
type t = {
mutable numDoors: int,
mutable colour: string
};
exception BadFerrari;
let setColour = (builder, colour) => builder.colour = colour;
let getColour = (builder) => builder.colour;
let setNumDoors = (builder, n) => builder.numDoors = n;
let getNumDoors = (builder) => builder.numDoors;
let getResult = (builder) =>
if (builder.numDoors == 2) {
Car.create(~make="Ferrari", ~model="488 Spider",
~numDoors=2, ~colour=builder.colour)
} else {
raise(BadFerrari)
};
};
最后,让我们实现一个director。
module Director = (Car: Car, Builder: CarBuilder with type car = Car.t) => {
let construct = (builder) => {
Builder.setColour(builder, "red");
Builder.setNumDoors(builder, 2)
};
};
我会让你实现用户代码作为练习。提示,您需要从接口的具体实现开始Car
。您可以在Try Reason中查看并使用代码(包括 OCaml 和 Javascript 版本)。