面向过程,面向对象,函数式对同一个问题的思考方式

开发 后端
我之所以对函数式代码感兴趣是因为函数式代码富有表现力,可以使用简短、紧凑的代码完成工作,同时能对特定的问题给出优雅的解决方案。现代的编程语言不约而同的朝着面向对象、函数式、动态、解释执行的方向发展,例如Ruby,Swift。

我之所以对函数式代码感兴趣是因为函数式代码富有表现力,可以使用简短、紧凑的代码完成工作,同时能对特定的问题给出优雅的解决方案。现代的编程语言不约而同的朝着面向对象、函数式、动态、解释执行的方向发展,例如Ruby,Swift。而另一些语言则更加强调函数式编程,如F#,Scala,这种语言有着强大的类型推断系统,编写的代码洁程度则令人叹为观止。

在F#编写一个两个数相加的函数,在F# Interactive中输入:

1
let add num1 num2=num1*num2;;

F# Interactive为我们推断了这个函数类型:val add : num1:int -> num2:int -> int,表示add有两个int类型的参数得到了1个int类型。

函数当作参数:

1
2
3
4
5
6
//C#
private int Twice(int input,Func<int,int> f)
{
    return f(f(input));
}
var result = Twice(2, n => n*n);

使用F#则只需要非常简洁的一个函数声明:

1
2
3
4
5
6
> 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.显示活动的宣传内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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. 延期活动:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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(…))之类的代码马上会提高警惕,是不是有一些抽象类型没有被找出来?在这个例子中则会找出下面的抽象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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可区分联合:

1
2
3
4
type Schedule=
| Once of DateTime
| Daily of DateTime*int
| Weekly of DateTime*int

这个类型既说明了Schedule有三个不同的类型,同时定义了三种类型分别具有的数据结构。像是Enum和类的综合体,但是又显得特别精致。

1.显示活动的宣传内容,使用了模式匹配:

1
2
3
4
5
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. 延期活动:

1
2
3
4
5
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()。通过定义可区分联合和模式匹配来完成编码,整个解决方案像是面向过程和面向对象的一种结合体,但是侧重点不同,实现的代码也更加精致。

责任编辑:李英杰 来源: 博客园
相关推荐

2016-12-15 08:54:52

线程sessionopenSession

2022-07-30 23:41:53

面向过程面向对象面向协议编程

2010-07-15 13:56:24

面向对象面向过程

2023-11-30 08:00:54

面向对象面向切面

2013-11-26 10:14:15

面向对象函数式

2009-07-08 16:10:24

Scala简介面向对象函数式

2009-09-27 15:29:00

Scala讲座面向对象Scala

2022-03-26 09:32:54

Python编程映射

2023-01-10 09:38:09

面向对象系统

2011-06-02 09:47:11

C语言重构

2020-10-26 12:27:18

对象编程函数式

2021-05-06 21:49:56

索引扫描次序

2011-01-19 10:50:31

软件设计师

2010-07-08 13:35:39

UML面向对象

2021-08-16 20:48:34

嵌入式单片机信息

2011-05-25 11:15:02

Javascript继承

2016-12-20 13:55:52

2013-03-11 09:23:22

Go语言面向对象

2023-02-22 18:06:35

函数javascript面向对象编程

2009-06-09 12:38:12

NetBeanseclipse
点赞
收藏

51CTO技术栈公众号