<commit_before>---
layout: post
title: 面向对象与函数式编程设计模式 
published: true
---


## 引子

坊间传闻很多程序员之间的鄙视链，我们虽然可以不以为然，但也常常不能免俗。在今天的面向对象设计与函数式编程的上下文中套用一下鄙视链的逻辑，可能是懂 Functional Programming 的工程师鄙视老是把设计模式挂在嘴边的工程师，老是把设计模式挂在嘴边的工程师鄙视会说「你这样写就不 OO 了啊」的工程师，会说「你这样写就不 OO 了啊」的工程师鄙视会说「蛤？什么面向对象？不是把重复的 code 写成一个 function 就好了吗？」的工程师，会说「蛤？什么面向对象？不是把重复的 code 写成一个 function 就好了吗？」的工程师鄙视把同一段 code 到处复制贴上的工程师。那么是么是面向对象，什么是函数式编程？他们之间又有怎样的关系呢？且看下文。

## 索引

* [概念](#概念)
* [面向对象与函数式编程对比（OO VS FP）](#面向对象与函数式编程对比oo-vs-fp)
* [面向对象编程核心](#面向对象编程核心)
  * [OO三大特性](#oo三大特性)
  * [OO设计原则（7个）](#oo设计原则7个)
  * [OO设计模式(23个)](#oo设计模式23个)
  * [戴着模式的眼镜再看MVC架构](#戴着模式的眼镜再看MVC架构)
* [函数式编程核心](#函数式编程核心)
  * [不可变的数据（Immutable Data）](#不可变的数据immutable-data)
  * [高阶函数（High Order Function）](#高阶函数high-order-function)
  * [FP设计模式](#fp设计模式)
* [函数式编程的优点](#函数式编程的优点)

## 概念

什么是面向对象？看下维基百科的定义： 面向对象编程（英语：Object-oriented programming，缩写：OOP）是种具有对象概念的程序编程范型，同时也是一种程序开发的方法。它可能包含数据、属性、代码与方法。对象则指的是类的实例。它将对象作为程序的基本单元，将程序和数据封装其中，以提高软件的重用性、灵活性和扩展性，对象里的程序可以访问及经常修改对象相关连的数据。在面向对象程序编程里，计算机程序会被设计成彼此相关的对象。

什么是函数式编程？继续摘抄维基百科的定义：函数式编程（英语：functional programming）或称函数程序设计，又称泛函编程，是一种编程范型，它将电脑运算视为数学上的函数计算，并且避免使用程序状态以及易变对象。函数编程语言最重要的基础是λ演算（lambda calculus）。而且λ演算的函数可以接受函数当作输入和输出。比起命令式编程，函数式编程更加强调程序执行的结果而非执行的过程，倡导利用若干简单的执行单元让计算结果不断渐进，逐层推导复杂的运算，而不是设计一个复杂的执行过程。

## 面向对象与函数式编程对比（OO VS FP）

从起源来看，面向对象来源于生物学，将程序用对象的形式来表示。而函数式编程来源于数学，具体说是来源于[lambda演算](https://zh.wikipedia.org/wiki/%CE%9B%E6%BC%94%E7%AE%97)。
而OO和FP的被人们广泛应用则是分别是因为GUI应用和并行计算的兴起。从程序的组织形式上看，OO和FP编程范式的程序的基本单位分别是对象和函数，OO是指令式编程，而FP是声明式编程。OO的代表语言有C++，Java，Objective-C， C#，而FP的代表语言有Haskell，Scheme，Erlang，Ocaml，而另外一些编程语言（如JavaScript和Python）既可以采用面向对象编程又可以采用函数式编程范型。

## 面向对象编程核心

### OO三大特性
对象是现实世界的模型。面向对象编程的三个基本要素是封装、继承和多态。
封装即利用抽象数据类型将数据和对数据的操作包装在一起，使其构成一个不可分割的独立体。数据被保护在抽象数据类型的内部，尽可能地隐藏内部的细节，只保留一些对外接口使之与外部发生联系。系统的其他对象只能通过包裹在数据外面的已经授权的操作来与这个封装的对象进行交流和交互。也就是说用户是无需知道对象内部的细节，但可以通过该对象对外提供的接口来访问该对象。
继承是使用已存在的类的定义作为基础建立新类的技术，新类的定义可以增加新的数据或新的功能，也可以使用用父类的属性和功能。通过使用继承我们能够非常方便地复用以前的代码，提高开发效率。
所谓多态是指向在继承中，子类重写了父类的方法，而父类数据类型的引用指向子类的对象（向上转型），使得父类数据类型的引用可以用统一的代码实现不同的逻辑，表现出不同的行为。

### OO设计原则（7个）

除了高内聚、低耦合、关注点分离、模块化开发等软件设计的一些通用原则外，面向对象编程一般还遵循7个重要的原则。即SOLID原则（单一职责原则、开放封闭原则、里氏替换原则、依赖导致原则、接口分离原则），最少知识原则（迪米特法则）和合成复用原则。

#### 单一职责原则(Single Responsibility Principle)
一个对象应该只包含单一定职责，并且该职责被完整地封装在一个类中。    

#### 开闭原则(Open Closed Principle)  
一个软件实体应当对扩展开放，对修改关闭，也就是说在设计一个模块或类的时候，应当使这个模块可以在不被修改的前提下被扩展，即实现在不修改源代码的情况下改变这个模块的行为。 

#### 里氏代换原则(Liskov Substitution Principle)  
所有引用基类的地方必须能够透明地使用其子类地对象。也就是继承必须确保基类所拥有的性质在子类中仍然成立。LSP是继承复用的基石，只有当衍生类可以替换掉基类，软件单位的功能不受到影响时，基类才能真正被复用，而衍生类也能够在基类的基础上增加新的行为。

#### 依赖倒转原则(Dependency Inversion Principle) <commit_msg>fix typo<commit_after>---
layout: post
title: 面向对象与函数式编程设计模式 
published: true
---


## 引子

坊间传闻很多程序员之间的鄙视链，我们虽然可以不以为然，但也常常不能免俗。在今天的面向对象设计与函数式编程的上下文中套用一下鄙视链的逻辑，可能是懂 Functional Programming 的工程师鄙视老是把设计模式挂在嘴边的工程师，老是把设计模式挂在嘴边的工程师鄙视会说「你这样写就不 OO 了啊」的工程师，会说「你这样写就不 OO 了啊」的工程师鄙视会说「蛤？什么面向对象？不是把重复的 code 写成一个 function 就好了吗？」的工程师，会说「蛤？什么面向对象？不是把重复的 code 写成一个 function 就好了吗？」的工程师鄙视把同一段 code 到处复制贴上的工程师。那么什么是面向对象，什么是函数式编程？他们之间又有怎样的关系呢？

## 索引

* [概念](#概念)
* [面向对象与函数式编程对比（OO VS FP）](#面向对象与函数式编程对比oo-vs-fp)
* [面向对象编程核心](#面向对象编程核心)
  * [OO三大特性](#oo三大特性)
  * [OO设计原则（7个）](#oo设计原则7个)
  * [OO设计模式(23个)](#oo设计模式23个)
  * [戴着模式的眼镜再看MVC架构](#戴着模式的眼镜再看MVC架构)
* [函数式编程核心](#函数式编程核心)
  * [不可变的数据（Immutable Data）](#不可变的数据immutable-data)
  * [高阶函数（High Order Function）](#高阶函数high-order-function)
  * [FP设计模式](#fp设计模式)
* [函数式编程的优点](#函数式编程的优点)

## 概念

什么是面向对象？看下维基百科的定义： 面向对象编程（英语：Object-oriented programming，缩写：OOP）是种具有对象概念的程序编程范型，同时也是一种程序开发的方法。它可能包含数据、属性、代码与方法。对象则指的是类的实例。它将对象作为程序的基本单元，将程序和数据封装其中，以提高软件的重用性、灵活性和扩展性，对象里的程序可以访问及经常修改对象相关连的数据。在面向对象程序编程里，计算机程序会被设计成彼此相关的对象。

什么是函数式编程？维基百科的定义：函数式编程（英语：functional programming）或称函数程序设计，又称泛函编程，是一种编程范型，它将计算机运算视为数学上的函数计算，并且避免使用程序状态以及易变对象。函数编程语言最重要的基础是λ演算（lambda calculus）。而且λ演算的函数可以接受函数当作输入和输出。比起命令式编程，函数式编程更加强调程序执行的结果而非执行的过程，倡导利用若干简单的执行单元让计算结果不断渐进，逐层推导复杂的运算，而不是设计一个复杂的执行过程。

## 面向对象与函数式编程对比（OO VS FP）

从起源来看，面向对象来源于生物学，将程序用对象的形式来表示。而函数式编程来源于数学（具体是[lambda演算](https://zh.wikipedia.org/wiki/%CE%9B%E6%BC%94%E7%AE%97)）。
而OO和FP的被人们广泛应用则是分别是因为GUI应用和并行计算的兴起。从程序的组织形式上看，OO和FP编程范式的程序的基本单位分别是对象和函数，OO是指令式编程，而FP是声明式编程。OO的代表语言有C++，Java，Objective-C， C#，而FP的代表语言有Haskell，Scheme，Erlang，而另外一些编程语言（如JavaScript和Python）既可以采用面向对象编程又可以采用函数式编程范型。

## 面向对象编程核心

### OO三大特性
对象是现实世界的模型。面向对象编程的三个基本要素是封装、继承和多态。
封装即利用抽象数据类型将数据和对数据的操作包装在一起，使其构成一个不可分割的独立体。数据被保护在抽象数据类型的内部，尽可能地隐藏内部的细节，只保留一些对外接口使之与外部发生联系。系统的其他对象只能通过包裹在数据外面的已经授权的操作来与这个封装的对象进行交流和交互。也就是说用户是无需知道对象内部的细节，但可以通过该对象对外提供的接口来访问该对象。
继承是使用已存在的类的定义作为基础建立新类的技术，新类的定义可以增加新的数据或新的功能，也可以使用用父类的属性和功能。通过使用继承我们能够非常方便地复用以前的代码，提高开发效率。
所谓多态是指向在继承中，子类重写了父类的方法，而父类数据类型的引用指向子类的对象（向上转型），使得父类数据类型的引用可以用统一的代码实现不同的逻辑，表现出不同的行为。

### OO设计原则（7个）

除了高内聚、低耦合、关注点分离、模块化开发等软件设计的一些通用原则外，面向对象编程一般还遵循7个重要的原则。即SOLID原则（单一职责原则、开放封闭原则、里氏替换原则、依赖导致原则、接口分离原则），最少知识原则（迪米特法则）和合成复用原则。

#### 单一职责原则(Single Responsibility Principle)
一个对象应该只包含单一定职责，并且该职责被完整地封装在一个类中。    

#### 开闭原则(Open Closed Principle)  
一个软件实体应当对扩展开放，对修改关闭，也就是说在设计一个模块或类的时候，应当使这个模块可以在不被修改的前提下被扩展，即实现在不修改源代码的情况下改变这个模块的行为。 

#### 里氏代换原则(Liskov Substitution Principle)  
所有引用基类的地方必须能够透明地使用其子类地对象。也就是继承必须确保基类所拥有的性质在子类中仍然成立。LSP是继承复用的基石，只有当衍生类可以替换掉基类，软件单位的功能不受到影响时，基类才能真正被复用，而衍生类也能够在基类的基础上增加新的行为。

#### 依赖倒转原则(Dependency Inversion Principle) 
{'fraction_non_alphanumeric': 0.11662198391420911, 'fraction_numerical': 0.005744925315970892, 'mean_word_length': 14.731927710843374, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5655888', 'n_tokens_mistral': 4417, 'n_tokens_neox': 5142, 'n_words': 161}