从一个加法器看简单的编程范式

从一个加法器的实现看简单的编程范式

编程范式

编程范式(Programming Paradigm)是某种编程语言典型的编程风格或者说是编程方式。随着编程方法学和软件工程研究的深入,特别是OO思想的普及,范式(Paradigm)以及编程范式等术语渐渐出现在人们面前。面向对象编程(OOP)常常被誉为是一种革命性的思想,正因为它不同于其他的各种编程范式。类似面向过程编程、面向对象编程、函数式编程等都称为编程范式。

从一个加法器写起

需要实现一个加法器:在这个加法器中,已经保存了被加数;现在需要传递加数给这个加法器,以让其返回加法计算结果。

变化:现在需要给被加数添加一个权重值;但是以前的加法器仍需保留,因为还有一部分代码会使用它。

方案一

首先实现加法器,利用一个结构体来保存被加数,再写一个加法函数,通过struct SLAugend *pSLAugend来访问结构体,进而通过pSLAugend->iAugend访问被加数,访问结构体的方式则是以指针访问、地址传参的形式进行。

#include <iostream>
using namespace std;
struct SLAugend
{
    int iAugend;
};

int Add(struct SLAugend *pSLAugend, int iAddend)
{
    return pSLAugend->iAugend + iAddend;
}

int main()
{
    struct SLAugend augend;

    augend.iAugend = 2;

    cout << Add(&augend, 5) << endl;

    return 0;
}

可能会觉得这样的代码有些赘余,明明不用通过指针的形式访问结构体的,可以直接通过Add2(augend.iAugend,5)来得出结果;确实如此,不过按这样说一个加法器实际上都用不到结构体呢。。。这里只是为了便于理解编程范式所举的例子,而且这样子的好处在之后的代码中就显现出来了,对于变化后的代码:

#include <iostream>

using namespace std;

struct SLAugend
{
    int iAugend;
};

int Add(struct SLAugend *pSLAugend, int iAddend)
{
    return pSLAugend->iAugend + iAddend;
}

struct SLWeightingAugend
{
    int iAugend;
    int iWeight;
};

int WeightingAdd(struct SLWeightingAugend *pSLWeightingAugend, int iAddend)
{
    return pSLWeightingAugend->iWeight * pSLWeightingAugend->iAugend + iAddend;
}

int main()
{
    struct SLWeightingAugend augend;

    augend.iAugend = 2;
    augend.iWeight = 3;

    cout << WeightingAdd(&augend, 5) << endl;

    return 0;
}

我们增加了一个新结构体和新的加法函数,主函数基本没变化(只是增加了权重变量的值和结构体名称),emm,这段代码确实复杂而且赘余!

方案2

通过结构化思想所建立的代码不够完善,我们尝试利用基于对象的思想进行代码的重构,可以编写一个加法器的类,数据成员做为私有变量保存着,然后再写一个公有的加法方法:

#include <iostream>

using namespace std;

class CLAdder
{
public:
    explicit CLAdder(int iAugend)
    {
    m_iAugend = iAugend;
    }

    int Add(int iAddend)
    {
    return m_iAugend + iAddend;
    }

private:
    int m_iAugend;
};

int main()
{
    CLAdder adder(2);
    cout << adder.Add(4) << endl;

    return 0;
}

这段代码就比较清晰了,构造函数CLAdder会自动执行赋值的操作,而调用类的方法也是非常简单的:adder.Add(4)。然后我们再看看变化之后的代码:

#include <iostream>

using namespace std;

class CLWeightingAdder
{
public:
    CLWeightingAdder(int iAugend, int iWeight)
    {
    m_iAugend = iAugend;
    m_iWeight = iWeight;
    }

    int Add(int iAddend)
    {
    return m_iAugend * m_iWeight + iAddend;
    }

private:
    int m_iAugend;
    int m_iWeight;
};

int main()
{
    CLWeightingAdder adder(2, 3);
    cout << adder.Add(5) << endl;

    return 0;
}

仍然对部分老代码进行了修改,忽视了代码的封闭性,所以也不算上是好的设计。

方案3

好在面向对象思想里面为我们提供了一个“继承”与“虚函数”的概念,我们直接对变化进行处理,先看代码:

#include <iostream>

using namespace std;

class CLAdder
{
public:
    explicit CLAdder(int iAugend)
    {
    m_iAugend = iAugend;
    }

    virtual ~CLAdder()
    {
    }

    virtual int Add(int iAddend)
    {
    return m_iAugend + iAddend;
    }

protected:
    int m_iAugend;
};

class CLWeightingAdder : public CLAdder
{
public:
    CLWeightingAdder(int iAugend, int iWeight) : CLAdder(iAugend)
    {
    m_iWeight = iWeight;
    }

    virtual ~CLWeightingAdder()
    {
    }

    virtual int Add(int iAddend)
    {
    return m_iAugend * m_iWeight + iAddend;
    }

protected:
    int m_iWeight;
};

int main()
{
    CLAdder adder(2);
    cout << adder.Add(4) << endl;

    CLWeightingAdder wadder(3, 4);
    cout << wadder.Add(4) << endl;

    return 0;
}

CLAdder这个类里面提供了一个虚函数Add,然后通过class CLWeightingAdder : public CLAdder继承了基函数,在里面对虚函数进行重载,加上权重的功能。这段代码做到了代码的封闭性,也能封装变化点。

方案4

假定我们需求改变:1.需要实现一个加法器:在这个加法器中,已经保存了被加数;现在需要传递加数给这个加法器,以让其返回加法计算结果。2.普通加法器的被加数,必须是非负的整数,而带权重的加法器的被加数,没有任何限制.3,实现变化

换一个新的角度去思考,我们可以基于接口的思想,即定义一个加法接口的抽象类,然后让普通加法器和带权重的加法器从这个抽象类中派生出来,先看代码:

#include <iostream>

using namespace std;

class ILAdder
{
public:
    ILAdder()
    {
    }

    virtual ~ILAdder()
    {
    }

    virtual int Add(int iAddend) = 0;
};

class CLAdder : public ILAdder
{
public:
    explicit CLAdder(unsigned int iAugend)
    {
    m_iAugend = iAugend;
    }

    virtual ~CLAdder()
    {
    }

    virtual int Add(int iAddend)
    {
    return m_iAugend + iAddend;
    }

private:
    unsigned int m_iAugend;
};

class CLWeightingAdder : public ILAdder
{
public:
    CLWeightingAdder(int iAugend, int iWeight)
    {
    m_iWeight = iWeight;
    m_iAugend = iAugend;
    }

    virtual ~CLWeightingAdder()
    {
    }

    virtual int Add(int iAddend)
    {
    return m_iAugend * m_iWeight + iAddend;
    }

private:
    int m_iAugend;
    int m_iWeight;
};

void f(ILAdder *pAdder)
{
    cout << pAdder->Add(4) << endl;
}

int main()
{
    CLAdder adder(2);
    f(&adder);

    CLWeightingAdder wadder(3, 4);
    f(&wadder);

    return 0;
}

由于题目的需求更改为普通加法器的被加数,必须是非负的整数,而带权重的加法器的被加数,没有任何限制,所以需要对被加数的数据形式进行限定,可以将数据定义从基类中分离出来,在每个派生类中进行数据的定义与虚函数Add的重载,就能实现任务。

方案5

由于要涉及不同的编程范式,呢么我们不如再延伸一点,给予接口的思想模板实现。“模板”这个概念在C++中有着非同小可的意义,是泛型编程的重点,比如著名的STL库,肯定需要适配用户的不同需要,则时候通过模板就能满足这一点。让我们看看这一段代码:

#include <iostream>

using namespace std;

template<typename T>
class ILAdder
{
public:
    ILAdder()
    {
    }

    virtual ~ILAdder()
    {
    }

    int Add(int iAddend)
    {
    T *pThis = (T *)(this);
    return pThis->AddImpl(iAddend);
    }
};

class CLAdder : public ILAdder<CLAdder>
{
public:
    explicit CLAdder(unsigned int iAugend)
    {
    m_iAugend = iAugend;
    }

    virtual ~CLAdder()
    {
    }

    int AddImpl(int iAddend)
    {
    return m_iAugend + iAddend;
    }

private:
    unsigned int m_iAugend;
};

class CLWeightingAdder : public ILAdder<CLWeightingAdder>
{
public:
    CLWeightingAdder(int iAugend, int iWeight)
    {
    m_iWeight = iWeight;
    m_iAugend = iAugend;
    }

    virtual ~CLWeightingAdder()
    {
    }

    int AddImpl(int iAddend)
    {
    return m_iAugend * m_iWeight + iAddend;
    }

private:
    int m_iAugend;
    int m_iWeight;
};

template<typename T>
void f(ILAdder<T> *pAdder)
{
    cout << pAdder->Add(4) << endl;
}

int main()
{
    CLAdder adder(2);
    f(&adder);

    CLWeightingAdder wadder(3, 4);
    f(&wadder);

    return 0;
}

其中class CLAdder : public ILAdder<CLAdder>class CLWeightingAdder : public ILAdder<CLWeightingAdder>是将模板参数T设置为CLAdder与CLWeightingAdder。这是因为在ILAdder这个类当中有pThis->AddImpl(iAddend),调用AddImpl函数进行加法,但是ILAdder并不知道会用哪一个,所以需要通过模板参数定位T *pThis = (T *)(this);,定位后就会到相应的派生类当中执行加法程序。

方案6

还可以靠增加基类来扩展加法器,就是说执行加法程序的是一个派生类,而继承的是不同的基类来实现变化的需求,先看代码:

#include <iostream>

using namespace std;

template<typename T>
class CLAdder : public T
{
public:
    CLAdder() : T()
    {
    }

    virtual ~CLAdder()
    {
    }

    int Add(int iAddend)
    {
    T *pThis = (T *)(this);
    return pThis->AddImpl(iAddend);
    }
};

class CLNormalImpl
{
public:
    CLNormalImpl()
    {
    m_iAugend = 0;
    }

    void Set(unsigned int iAugend)
    {
    m_iAugend = iAugend;
    }

    virtual ~CLNormalImpl()
    {
    }

    int AddImpl(int iAddend)
    {
    return m_iAugend + iAddend;
    }

private:
    unsigned int m_iAugend;
};

class CLWeightingImpl
{
public:
    CLWeightingImpl()
    {
    m_iWeight = 0;
    m_iAugend = 0;
    }

    void Set(int iAugend, int iWeight)
    {
    m_iWeight = iWeight;
    m_iAugend = iAugend;
    }

    virtual ~CLWeightingImpl()
    {
    }

    int AddImpl(int iAddend)
    {
    return m_iAugend * m_iWeight + iAddend;
    }

private:
    int m_iAugend;
    int m_iWeight;
};

template<typename T>
void f(CLAdder<T> *pAdder)
{
    cout << pAdder->Add(4) << endl;
}

int main()
{
    CLAdder<CLNormalImpl> adder;
    adder.Set(2);
    f(&adder);

    CLAdder<CLWeightingImpl> wadder;
    wadder.Set(3, 4);
    f(&wadder);

    return 0;
}

class CLAdder : public T是继承一个模板参数,而模板参数是从主函数中获得的,和方案4刚好思路相反,一个是确定积累,通过扩展派生类实现;而这里是增加基类不动派生类。

总结

实际上,这些编程范式的变化和设计模式相对应起来了,因为代码的优化当中就是朝着更简便、重复更少取得,而设计模式里就是主要面向开发的,所以更为契合。

原文地址:https://www.cnblogs.com/yunlambert/p/9602244.html

时间: 2024-08-29 14:47:18

从一个加法器看简单的编程范式的相关文章

jQuery中的编程范式

浏览器前端编程的面貌自2005年以来已经发生了深刻的变化,这并不简单的意味着出现了大量功能丰富的基础库,使得我们可以更加方便的编写业务代码,更重要的是我们看待前端技术的观念发生了重大转变,明确意识到了如何以前端特有的方式释放程序员的生产力.本文将结合jQuery源码的实现原理,对javascript中涌现出的编程范式和常用技巧作一简单介绍.    1. AJAX: 状态驻留,异步更新      首先来看一点历史. A. 1995年Netscape公司的Brendan Eich开发了javacri

聊聊编程范式

编程语言有很多种流派和思想,有一些编程语言同时支持多种编程范式. 静态类型编程范式 采用静态类型编程范式的编程语言,其变量需要明确指定类型.代表语言:C,C++,Pascal,Objective-C,Java,C#,VB.NET,Swif,Golang. 这样做的好处是: 1.编译器可以在编译时就能找出类型错误. 2.编译器编译时知道类型信息,就可以提高性能. 这种范式认为,程序员肯定知道变量的类型,你丫要是不知道变量的类型,那你就别混了!编译时,程序会报错. Swift和Go语言都是静态类型编

编程范式与语言

这篇文章是应“编程新思路”COP小组之邀,对编程范式做一个专题分享.主要是自己在读书.学习.工作上的一些心得总结,能力有限,希望能抛砖引玉,一同探讨. 为什么要了解编程范式和语言 到目前为止,世界上约有2500种程序设计语言,其中一部分的族谱关系如下. 语言从世界观上思考和影响软件设计,不同范式看待设计的角度也迥然不同,比如命令范式语言以状态(变量)抽象现实世界,对象范式语言以对象对象现实世界,函数范式语言以计算(函数)抽象现实时间.对现实问题的不同观察视角,从根本上影响软件开发者的思考方式和对

函数式编程(一) 认识“编程范式”和“函数”

编程范式(Programming paradigm) 编程范式指我们在编写程序解决问题的思路和视角.它提供了同时也决定了程序员对程序运行的看法.计算机编程中存在许多编程范式,如命令式编程.声明式编程.面向对象编程以及结构化编程等等.其中面向对象编程范式认为程序是由一系列相互作用的对象组成,而结构化编程范式认为程序采用子程序.代码区块.for循环以及while循环等结构组成.下面主要说明本篇文章将要讲到的命令式编程范式和声明式编程范式. 1)命令式编程(Imperative): 强调程序代码模拟电

Python3学习之路~6.1 编程范式:面向过程 VS 面向对象

编程范式 编程是程序员用特定的语法+数据结构+算法组成的代码来告诉计算机如何执行任务的过程,一个程序是程序员为了得到一个任务结果而编写的一组指令的集合,正所谓条条大路通罗马,实现一个任务的方式有很多种不同的方式,对这些不同的编程方式的特点进行归纳总结得出来的编程方式类别,即为编程范式.不同的编程范式本质上代表对各种类型的任务采取的不同的解决问题的思路, 大多数语言只支持一种编程范式,当然也有些语言可以同时支持多种编程范式.两种最重要的编程范式分别是面向过程编程和面向对象编程. 面向过程编程(Pr

再谈编程范式—程序语言背后的思想

编程范式 托马斯.库尔提出“科学的革命”的范式论后,Robert Floyd在1979年图灵奖的颁奖演说中使用了编程范式一词.编程范式一般包括三个方面,以OOP为例: 1,学科的逻辑体系——规则范式:如 类/对象.继承.动态绑定.方法改写.对象替换等等机制. 2,心理认知因素——心理范式:按照面向对象编程之父Alan Kay的观点,“计算就是模拟”.OO范式极其重视隐喻(metaphor)的价值,通过拟人化,按照自然的方式模拟自然. 3,自然观/世界观——观念范式:强调程序的组织技术,视程序为松

转并修改:编程范式(Programming Paradigm)

编程范式(Programming Paradigm)是某种编程语言典型的编程风格或者说是编程方式.随着编程方法学和软件工程研究的深入,特别是OO思想的普及,范式(Paradigm)以及编程范式等术语渐渐出现在人们面前.面向对象编程(OOP)常常被誉为是一种革命性的思想,正因为它不同于其他的各种编程范式.编程范式也许是学习任何一门编程语言时要理解的最重要的术语. 托马斯.库恩提出“科学的革命”的范式论之后,Robert Floyd在1979年图灵奖的颁奖演说中使用了编程范式一词.编程范式一般包括三

编程范式

编程范式(Programming Paradigm)是某种编程语言典型的编程风格或者说是编程方式.随着编程方法学和软件工程研究的深入,特别是OO思想的普及,范式(Paradigm)以及编程范式等术语渐渐出现在人们面前.面向对象编程(OOP)常常被誉为是一种革命性的思想,正因为它不同于其他的各种编程范式.编程范式也许是学习任何一门编程语言时要理解的最重要的术语. 托马斯.库恩提出“科学的革命”的范式论之后,Robert Floyd在1979年图灵奖的颁奖演说中使用了编程范式一词.编程范式一般包括三

[编译]响应式编程范式--(1)

什么是响应式编程 在网络上有很多对响应式编程进行定义的资料.维基百科上的定义过于宽泛和理论化.Stackoverflow专业的回答,显然不适合刚接触响应式编程的用户. 响应式编程范式,就是使用异步的数据流进行开发 从这个角度看待它,它并不是新东西.事件总线或者典型的界面点击事件,它们就是异步的事件流,你可以监听要发生的事件,然后当事件发生的时候,再执行对应的操作.响应跟这个概念类似.你可以给任何事物创建数据流,而不只是对点击和鼠标悬停事件创建数据流.流是便宜和无处不在的,任何东西都可以被组织为流