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

我之所以对函数式代码感兴趣是因为函数式代码富有表现力,可以使用简短、紧凑的代码完成工作,同时能对特定的问题给出优雅的解决方案。现代的编程语言不约而同的朝着面向对象、函数式、动态、解释执行的方向发展,例如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()。通过定义可区分联合和模式匹配来完成编码,整个解决方案像是面向过程和面向对象的一种结合体,但是侧重点不同,实现的代码也更加精致。

来源:51CTO

时间: 2024-10-24 06:02:40

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

PHP中的面向对象和面向过程

对象|过程 简介"真正的天才具有正确评价不确定的,有风险的和矛盾的信息的能力.--邱吉尔" 使用许多编程语言时,你通常只能使用面向对象或面向过程二者之一的编程方式.而在PHP中,你可以自由选择或混用.目前绝大多数PHP程序员使用面向过程的方式,因为解析WEB页面本身就非常"过程化"(从一个标签到另一个标签).在HTML中嵌入过程处理代码是很直接自然的作法,所以PHP程序员通常使用这种方式. 如果你是刚接触PHP,用面向过程的风格来书写代码很可能是你唯一的选择.但是如

面向过程与面向对象的简单比较(个人心得)!

比较|对象|过程|心得 最近打开我以前做的一个项目,系统结构中使用了4个包含文件对登录用户的权限进行判断,属典型的面向过程写法,可能很多朋友以前都写过这样的代码.我把这些代码整理了一下,写成一个权限判断的简单类,以比较一个面各对象和面向过程之间的差异.代码如下(其中省略了部分代码). sesson1.php<?php/* * 功能:取得用户的cookie,以判断用户是否已经登录,并是否具有系统管理员权限 * 程序员:xiangli * 日期:2002-07-19 */ $UserName = $

算24点程序:面向过程与面向对象的C++

1.概述 给定4个整数,其中每个数字只能使用一次:任意使用 + - * / ( ) ,构造出一个表达式,使得最终结果为24,这就是常见的算24点的游戏.这方面的程序很多,一般都是穷举求解.本文介绍一种典型的算24点的程序算法,并给出两个具体的算24点的程序:一个是面向过程的C实现,一个是面向对象的java实现. 2.基本原理 基本原理是穷举4个整数所有可能的表达式,然后对表达式求值. 表达式的定义: expression = (expression|number) operator (expre

java-通俗解释:什么是面向对象?与面向过程区别在哪?

问题描述 通俗解释:什么是面向对象?与面向过程区别在哪? 什么是面向对象?与面向过程区别在哪?用通俗的话语解答,不要理论性的专业术语,最好是拿生活中的熟知的事物去类比解释!!!多谢~ 解决方案 面向对象和面向过程不矛盾,事实上,Java也是面向过程的编程语言. 面向过程是指,允许在程序中定义函数或者方法.也许你觉得奇怪,难道还有语言不能定义函数方法么?早期的basic就不可以,只能用跳转来实现函数调用. 面向对象更近一步,允许你将"过程"(函数.方法)以及它们的上下文相关的数据封装成对

面向对象与面向过程有什么区别

问题描述 面向对象与面向过程有什么区别 面向过程难道就是面向对象的执行过程吗?这感觉有点像按词解释新手请教 解决方案 面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了. 面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为. 例如五子棋,面向过程的设计思路就是首先分析问题的步骤:1.开始游戏,2.黑子先走,3.绘制画面,4.判断输赢,5.轮到白子,6.绘制画面,7.

跪求大神解答-面向对象与面向过程的区别?

问题描述 面向对象与面向过程的区别? 什么是面向对象?什么是面向过程?两者有什么区别?有什么优缺点?两者都有什么应用? 解决方案 ? ?面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了. 面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为. 例如五子棋,面向过程的设计思路就是首先分析问题的步骤:1.开始游戏,2.黑子先走,3.绘制画面,4.判断输赢,5......

【C/C++学院】0817-递归汉诺塔 双层递归 /CPP结构体 /面向过程与面向对象的编程模式/类的常识共用体实现一个类的特征/QT应用于类以及类的常识

递归汉诺塔 双层递归 #include <iostream> void han(int n, char A, char B, char C) { static int num = 1; std::cout << "第" << num << "次"; num++; if (n<1) { return; } else { han(n - 1, A, C, B); std::cout << A <&l

开发-用面向对象的语言进行面向过程的编程

问题描述 用面向对象的语言进行面向过程的编程 教研室的项目多为开发MIS,开发过程中采用 结构化的开发方法(结构化的分析+结构化的设计),但是编程却采用.Net平台,使用C#语言,C#语言是一种纯的面向对象的语言呀,在定义类的时候,我们程序员都是自己根据自己的感觉去定义.这样会不会有不妥的地方呀? 解决方案 面向对象的语言天生也是面向过程的语言. 如果你用C#,完全可以定义一个类,然后将所有方法都定义在里面,这样就特异化成了面向过程了. 解决方案二: 所谓特异化,好比等边三角形是正多边形的特例.

面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了;面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为(转)

谈谈你对面向对象的理解 面向对象编程,即OOP,是一种编程范式,满足面向对象编程的语言,一般会提供类.封装.继承等语法和概念来辅助我们进行面向对象编程. 面向对象是基于万物皆对象这个哲学观点. 所谓的面向对象就是将我们的程序模块化,对象化,把具体事物的特性属性和通过这些属性来实现一些动作的具体方法放到一个类里面 面向对象的三大特征 继承,封装,多态 一 继承 继承概念:一个类继承另一个类,则称继承的类为子类,被继承的类为父类. 目的:实现代码的复用. 理解:子类与父类的关系并不是日常生活中的父子