All the picture coming from http://fsharpforfunandprofit.com or http://www.slideshare.net
最近看到這個很有趣的設計概念,後來發現原來之前有介紹過的Functional Programming就是此概念的一種實作。
作者scottw其實是以F#介紹如何設計並完成一支程式,分為三篇文章:
- How to design and code a complete program
- Railway Oriented Programming,並提供影音及投影片介紹
- Organizing modules in a project
作者以一個簡單的use case當做範例,基本上是使用者更新資訊的一個處理過程:
轉個方向來看:
再延伸成:
基本上綠色成功路徑作者稱之為:Happy path,紅色錯誤路徑當然是Unhappy path了。
Happy path的程式可表示為:
一般加上簡單的錯誤處理或Log後可能會變為:
整個程式可能由單頁變成雙頁,對可讀性和維護性都會有些許的困擾,還好目前在C#至少有基於此概念的兩套函式庫可參考,介紹如下。
Vladimir Khorikov在Functional C#: Handling failures, input errors的文章
其NuGet。
Happy path:
[HttpPost]
public HttpResponseMessage CreateCustomer(string name, string billingInfo)
{
Customer customer = new Customer(name);
_repository.Save(customer);
_paymentGateway.ChargeCommission(billingInfo);
_emailSender.SendGreetings(name);
return new HttpResponseMessage(HttpStatusCode.OK);
}
Error Handling:
blablabla…
[HttpPost]
public HttpResponseMessage CreateCustomer(string name, string billingInfo)
{
Result<CustomerName> customerNameResult = CustomerName.Create(name);
if (customerNameResult.Failure)
{
_logger.Log(customerNameResult.Error);
return Error(customerNameResult.Error);
}
Result<BillingInfo> billingIntoResult = BillingInfo.Create(billingInfo);
if (billingIntoResult.Failure)
{
_logger.Log(billingIntoResult.Error);
return Error(billingIntoResult.Error);
}
try
{
_paymentGateway.ChargeCommission(billingIntoResult.Value);
}
catch (FailureException)
{
_logger.Log(“Unable to connect to payment gateway”);
return Error(“Unable to connect to payment gateway”);
}
Customer customer = new Customer(customerNameResult.Value);
try
{
_repository.Save(customer);
}
catch (SqlException)
{
_paymentGateway.RollbackLastTransaction();
_logger.Log(“Unable to connect to database”);
return Error(“Unable to connect to database”);
}
_emailSender.SendGreetings(customerNameResult.Value);
return new HttpResponseMessage(HttpStatusCode.OK);
}
ROP way
[HttpPost]
public HttpResponseMessage CreateCustomer(string name, string billingInfo)
{
Result<BillingInfo> billingInfoResult = BillingInfo.Create(billingInfo);
Result<CustomerName> customerNameResult = CustomerName.Create(name);
return Result.Combine(billingInfoResult, customerNameResult)
.OnSuccess(() => _paymentGateway.ChargeCommission(billingInfoResult.Value))
.OnSuccess(() => new Customer(customerNameResult.Value))
.OnSuccess(
customer => _repository.Save(customer)
.OnFailure(() => _paymentGateway.RollbackLastTransaction())
)
.OnSuccess(() => _emailSender.SendGreetings(customerNameResult.Value))
.OnBoth(result => Log(result))
.OnBoth(result => CreateResponseMessage(result));
}
噹噹!看到這裡你的內心應該會感動很多下吧…
stormy-ua的Railway
其NuGet。
ROP way
// combine validation functions
var combinedValidation = Railway
// log inpiut request
.Apply<Request> (r => LogRequest(r))
// do email and name validation in parallel and combine errors
.OnSuccess(
(r1, r2) => r1,
(e1, e2) => new AggregateException(e1, e2),
r => ValidateName(r),
r => ValidateEmail(r)
)
// extract request name
.OnSuccess (request => request.Name)
// log extracted name
.OnSuccess (name => Console.WriteLine ("Name: {0}", name))
// append dash to name
.OnSuccess (name => name + "-")
// log name
.OnSuccess (name => Console.WriteLine ("Name: {0}", name))
// make nume uppercase
.OnSuccess (name => name.ToUpper ())
// log name
.OnSuccess (name => Console.WriteLine ("Name: {0}", name))
// log failure if any occured during the pipeline execution
.OnFailure (e => Console.WriteLine ("Failure: {0} ", e.Message));
// invoke combined function
var result = combinedValidation (new Request { Name = "", Email = "" });
//var result = combinedValidation (new Request { Name = "", Email = "a@b.c" });
//var result = combinedValidation (new Request { Name = "Kirill", Email = "" });
//var result = combinedValidation (new Request { Name = "Kirill", Email = "a@b.c" });
// process result
switch (result.IsSuccess) {
case true:
Console.WriteLine ("Success. {0}", result.Value);
break;
case false:
Console.WriteLine ("Failure: {0}", result.Error);
break;
}
基本上我會選用第一種,不過這兩個函式庫的實作方式都值得瞭解。
Written with StackEdit.
RRB Chandigarh Result
回覆刪除