面向过程,面向对象,函数式对同一个问题的思考方式
来源:互联网 发布:淘宝网天猫食品 编辑:程序博客网 时间:2024/05/29 11:03
原文链接:
http://www.cnblogs.com/richieyang/p/4884103.html
我之所以对函数式代码感兴趣是因为函数式代码富有表现力,可以使用简短、紧凑的代码完成工作,同时能对特定的问题给出优雅的解决方案。现代的编程语言不约而同的朝着面向对象、函数式、动态、解释执行的方向发展,例如Ruby,Swift。而另一些语言则更加强调函数式编程,如F#,Scala,这种语言有着强大的类型推断系统,编写的代码洁程度则令人叹为观止。
在F#编写一个两个数相加的函数,在F# Interactive中输入:
let
add num1 num2=num1*num2;;
F# Interactive为我们推断了这个函数类型:val add : num1:int -> num2:int -> int,表示add有两个int类型的参数得到了1个int类型。
函数当作参数:
//C#
private
int
Twice(
int
input,Func<
int
,
int
> f)
{
return
f(f(input));
}
var
result = Twice(2, n => n*n);
使用F#则只需要非常简洁的一个函数声明:
>
let
twice (input:int) f=f(f(input));;
val
twice : input:int -> f:(int -> int) -> int
> twice 2 (
fun
n->n*n);;
val
it : int = 16
val twice : input:int -> f:(int -> int) –> int 这句话则是F# Interactive给出的推断:twice函数需要一个int参数和一个(int->int)的函数作为参数,返回一个int.
这两个例子仅仅是热身,并不是本篇博客的重点,所以你觉得前两个例子很无聊或者没太看明白请继续看下面的总结。
场景:某种活动会有一个日程安排(Schedule),日程安排有3中类型,只举办一次(Once),每天一次(Daily),每周一次(Weekly)。活动会根据日程安排(Schedule)的类型不同具有不同的宣传内容,不同的延期举行策略。
你对于这样的场景会有怎么样的思考呢?
一、面向过程类型的编码方式
面向过程类型的编码是需求的直译过程,代码会写成这样:
1.显示活动的宣传内容:
public
void
ShowScheduleDescriptions()
{
switch
(ScheduleType)
{
case
ScheduleType.Once:
Console.WriteLine(
"this is once activity"
);
break
;
case
ScheduleType.Daily:
Console.WriteLine(
"this is daily activity"
);
break
;
case
ScheduleType.Weekly:
Console.WriteLine(
"this is weekly activity"
);
break
;
default
:
throw
new
InvalidOperationException(
"unsupported schedule"
);
}
}
这样的代码初次看起来没什么问题,实际存在两个危险信号:
- 违反开放封闭(OCP)原则,如果有一天需要加入一种Monthly类型,无疑需要修改这个方法;
- 这样的代码风格会让接下来的开发者不假思索的进行延续,比方说需要根据不同的活动类型延期活动;
2. 延期活动:
public
void
DelaySchedule()
{
switch
(ScheduleType)
{
case
ScheduleType.Once:
Console.WriteLine(
"Delay one hour"
);
break
;
case
ScheduleType.Daily:
Console.WriteLine(
"Delay one day"
);
break
;
case
ScheduleType.Weekly:
Console.WriteLine(
"Delay one week"
);
break
;
default
:
throw
new
InvalidOperationException(
"unsupported schedule"
);
}
}
这样的代格违反了DRY原则,相同的代码框架却无法重用。
二、面向对象的编码方式
对于一个有经验的OO开发者,一旦看到switch,if(type=typeof(…))之类的代码马上会提高警惕,是不是有一些抽象类型没有被找出来?在这个例子中则会找出下面的抽象:
public
class
Schedule
{
public
virtual
void
ShowShowScheduleDescriptions()
{
}
public
virtual
void
DelaySchedule()
{
}
}
public
class
OnceSchedule : Schedule
{
public
override
void
ShowShowScheduleDescriptions()
{
Console.WriteLine(
"this is once activity"
);
}
public
override
void
DelaySchedule()
{
Console.WriteLine(
"Delay one hour"
);
}
}
public
class
DailySchedule : Schedule
{
public
override
void
ShowShowScheduleDescriptions()
{
Console.WriteLine(
"this is daily activity"
);
}
public
override
void
DelaySchedule()
{
Console.WriteLine(
"Delay daily day"
);
}
}
//... other schedule
这样的代码很好的解决了面向过程代码的两个问题,看起来更加具有扩展性,随着新类型的Schedule引入,旧的代码完全不用改动。
当然事情也不是绝对的,什么情况下需要改动旧代码呢?当需要扩展Schedule的行为的时候,例如需求升级,不同的Schedule具有不同的举办方式,我们不得不在每种Schedule中加入一个 void Hold()方法。
三、函数式解决方案
函数式语言则使用可区分联合和模式匹配来处理此类问题。
定义一个Schedule可区分联合:
type
Schedule=
| Once
of
DateTime
| Daily
of
DateTime*int
| Weekly
of
DateTime*int
这个类型既说明了Schedule有三个不同的类型,同时定义了三种类型分别具有的数据结构。像是Enum和类的综合体,但是又显得特别精致。
1.显示活动的宣传内容,使用了模式匹配:
let
ShowShowScheduleDescriptions schedule=
match
schedule
with
| Once(DateTime)-> printfn
"this is once activity"
| Daily(DateTime,int)->printfn
"this is daily activity"
| Weekly(DateTime,int)->printfn
"this is weekly activity"
这个方法类似于switch…case,但是通过匹配可区分联合来实现,而不是通过一个显示的Enum来实现。
2. 延期活动:
let
DelaySchedule schedule=
match
schedule
with
| Once(DateTime)-> printfn
"Delay one hour"
| Daily(DateTime,int)->printfn
"Delay one day"
| Weekly(DateTime,int)->printfn
"Delay one week"
函数式编程的解决方案认为可以很方便的添加新的行为,例如增加新的行为:Hold()。通过定义可区分联合和模式匹配来完成编码,整个解决方案像是面向过程和面向对象的一种结合体,但是侧重点不同,实现的代码也更加精致。
- 面向过程,面向对象,函数式对同一个问题的思考方式
- 面向对象的思考问题方式
- 面向对象、面向过程的思考
- 面向对象思考过程
- java学习之旅29--面向对象_02_面向对象的方式思考问题
- 29_面向对象_02_面向对象的方式思考问题
- 面向过程与面向对象的本质区别思考
- 面向过程与面向对象的本质区别思考1
- 面向过程与面向对象的对比思考
- 面向对象与面向过程的一点思考
- 面向过程与面向对象的本质区别思考
- 面向对象的思考
- 面向对过程与面向对象
- 读《面向对象的思考过程》作者matt Weisfeld
- 面向过程 函数式 面向对象 ----基于python讲解
- 推荐学习面向对象程序设计的同学阅读:面向对象的思考过程 一书
- 面向对象的过程
- 面向对象的过程
- C++随记(五)---const 与 指针
- 实现WebService只返还json结构数据
- 冒泡排序实现升序和降序排列
- jsonp跨域请求
- Vue.js的搭建
- 面向过程,面向对象,函数式对同一个问题的思考方式
- 计算广告小结[下]
- 为Hexo博客的代码片段添加 选择全部 按钮
- java开学篇
- 卡特兰公式的应用
- 002_运行第一个容器
- ELK grok 调试工具
- Android 布局:相对布局
- ACdream 1056 Bad Horse (判断二分图)(并查集 or BFS)