起因
我最近确实在备战软考,其中发现许多的编程思路。本来就打算随便学学应付考试的,但是今天,学习rust的时候,学到一个面向函数编程,这让我一时间有了兴趣,仔细想想,如果可以系统级别学习一些编程思路,确实是可以增加我的眼见和对事物的理解,也方便我以为开创和使用更好的思路去编程,增加效率,所以还是写一篇来系统总结一下吧。
面向过程编程
面向过程是一种以事件为中心的编程思想,编程的时候把解决问题的步骤分析出来,然后用函数把这些步骤实现,在一步一步的具体步骤中再按顺序调用函数。我们都是有一个目标,然后不断设计多个函数,然后把一个一个过程组装起来,这就是面向过程编程。面向过程编程强调将程序功能分解成一系列可重用和有序执行的过程或函数。这种范式是从更早的结构化编程演变而来,侧重于明确的步骤和过程的顺序。
特点:
1.函数和过程:程序被分解成一组函数或过程,每个过程完成特定的任务。函数可以调用其他函数,实现复杂逻辑的分层和复用。
2.数据与逻辑分离:数据通常被定义为全局结构,而逻辑则在函数中处理。函数根据需要操作这些数据。
3.控制流:程序的执行流程通过结构化控制语句(如if条件语句、循环语句等)来管理。这种控制结构帮助管理复杂逻辑的执行顺序。
4.模块化:面向过程编程鼓励将程序分解成模块或单元,每个单元具有明确的功能。这些单元可以独立开发和测试,然后组合成完整的程序。
常见就是c语言。shell编程等
面向过程编程的优点:
1.简单性:面向过程的方法由于其直接性和透明性,通常更易于理解和实现,特别是对于简单的问题。
2.性能:过程调用通常比面向对象编程中的方法调用更快,因为它们操作的数据结构较少,且内存使用更直接。
3.复用性:通过函数,可以复用代码,减少重复代码的编写。
面向过程编程的缺点:
1.可扩展性问题:对于大型系统,面向过程编程可能导致代码难以管理和维护。随着系统复杂度的增加,修改程序可能会变得更加困难。
2.数据安全:由于数据通常是全局访问的,这可能会引起数据访问的问题,尤其是在多线程环境中。
3.代码重复:在大型项目中,可能会发现相似的代码块在不同的地方重复,增加了维护的负担。
应用:
面向过程编程适用于相对简单的、过程驱动的任务,如脚本编写、小型项目、或者在性能要求较高且逻辑较为直接的应用中。它在嵌入式系统、操作系统的开发和其他需要高性能、低资源消耗的环境中也非常普遍。
面向对象编程
这个就是模拟现实生活中的种种事物,每一个都可以看做一个对象,而每个对象都有自己的属性和行为,对象与对象之间通过方法来交互。面向对象是一种以“对象”为中心的编程思想,把要解决的问题分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个对象在整个解决问题的步骤中的属性和行为。好处就是可以多次复用,而且封装和使用方便。
特点:
1.封装:将数据(属性)和操作这些数据的代码(方法)捆绑在一起形成对象。这样做的主要优点是隐藏内部细节,只对
外提供有限的接口进行交互。
2.继承:允许新创建的对象继承现有对象的属性和方法。这促进了代码的重用,并可以建立一个层次化的对象模型。
3.多态性:允许不同的对象对同一消息作出响应。例如,父类有一个方法,通过不同的子类可以有不同的实现,但调用接
口(方法名)保持一致。
4.抽象:允许程序员使用类的实例创建具体的对象,并且使用接口定义非具体的操作。这样可以操作具有某些共同特征的对
象,而不需要考虑对象的具体类型。
优点:
1.提高软件开发效率:通过继承和重用已有的代码,可以减少重复代码的编写。
2.提高软件可维护性:封装使得代码更容易被理解,也更容易被修改,不会影响到无关的系统部分。
3.增强软件的灵活性:多态性允许系统更容易地扩展,可以在不改变现有系统的情况下引入新的对象类型。
缺点:
1.性能问题:面向对象系统可能会因为抽象和动态特性而牺牲一些性能。
2.过度设计:错误的或过度的使用面向对象设计可能导致系统复杂度过高,难以理解和维护。
3.学习曲线:对于初学者来说,理解所有OOP概念及其正确应用可能相对困难。
应用:面向对象编程由于其模块化和直观性,成为了现代软件工程中的一个基石。尽管有一些挑战和限制,但它在处理复杂
系统设计和团队协作方面的优势使其成为企业和大型项目的首选编程范式。
常见就是cpp python java rust那些的编程思路,其实也可以使用c语言的结构体里面使用函数指针,也可以实现面向对
象的存在,看linux的驱动开发就知道了,很多都是有操作集。
函数式编程
函数式编程(Functional Programming, FP)是一种编程范式,它将计算视为数学函数的评估,并避免使用程序状态以及可变对象。与面向对象编程侧重于对象(数据及其相关行为)不同,函数式编程强调的是无副作用的函数应用。
特点:
1.不可变性:在函数式编程中,变量一旦被赋值后就不会改变。任何修改都会产生新的数据结构而不是改变已有的。
2.函数是一等公民:函数可以作为参数传递,可以作为返回值,也可以被赋值给变量,这意味着函数的使用方式与其他数据类型无异。
3.纯函数:纯函数的输出只依赖于输入,并且不产生外部可观察的副作用(如修改全局变量或进行输入/输出操作)。
4.递归:由于函数式编程通常不使用循环(for 或 while),递归是实现循环逻辑的一种重要手段。
5.高阶函数:这些是接受其他函数作为参数或将函数作为结果返回的函数。
应用:
函数式编程语言通常提供一系列便利的功能来支持这些概念,如高阶函数、匿名函数(lambda 表达式)、惰性计算和模式匹配等。
许多现代编程语言,如 JavaScript、Python、Ruby、rust
面向数据结构
Jackson Structured Programming(JSP),这是一种系统的软件开发方法,主要用于结构化编程。Jackson Structured Programming由Michael A. Jackson在1970年代开发,用于设计高质量的结构化程序。JSP特别强调数据结构在软件设计中的重要性,并提供了一套方案来分析数据和根据这些数据的结构来设计程序。
特点:
1.数据驱动:在JSP中,设计过程从对数据结构的分析开始。这包括数据的组织、时序和处理方式。数据结构的理解直接影响程序结构的设计。
2.结构图:使用结构图(也称为Jackson图)来可视化数据和程序结构。这些图帮助设计者清晰地理解和规划程序的逻辑流程。
3.模块化设计:JSP鼓励将大的程序分解为小的、可管理的模块。每个模块都处理数据结构的一部分,并可以被单独开发和测试。
4.同步与变换:JSP中重要的概念是程序结构应该映射(或同步)到数据结构。如果数据结构发生变化,相应的程序结构也需要调整。
应用:广泛应用于数据密集型的应用领域,如金融服务、电信和企业数据处理等。
它特别适用于处理复杂的数据流和大规模数据集。
JSP是一种比较传统的方法,随着面向对象和其他现代编程范式的兴起,它的使用频率有所下降。
然而,对于一些特定的应用场景,特别是那些依赖于复杂数据处理的领域,JSP依然是一个有效的工具。
原型化编程
原型化编程(Prototype-based programming)是一种编程范式,它不依赖于类的概念,而是通过克隆现有的对象来创建新对象。这种范式是面向对象编程的一种变体,它消除了类和实例之间的区别,取而代之的是原型对象的概念。
特点:
1.无类(Classless):在原型编程中,没有类的概念。所有对象都是通过复制现有对象(称为原型)来创建的。
2.对象的克隆:新对象是通过复制一个原型对象生成的。这个过程可以包括对原型对象的属性和方法的复制。
3.动态性:对象可以在运行时动态地添加或修改属性和方法。这种灵活性是原型编程的一大优势,允许动态调整系统的行为。
4.直接的继承机制:一个对象可以作为其他对象的原型,允许属性和方法的直接继承。这与基于类的编程中通过类继承来实现的继承机制不同。
应用:
原型编程常用于那些需要高度灵活性和动态性的应用中,例如快速原型开发、动态环境(如Web开发)、游戏开发等领域。由于其在对象创建和继承方面的灵活性,它可以用来快速试验和迭代开发新的功能和对象模型。原型化编程提供了一种与基于类的编程截然不同的思考和实现面向对象编程的方法,通过实际操作和应用可以进一步理解和掌握它的概念和技术细节。
JavaScript:是最著名的支持原型编程的语言。在JavaScript中,几乎所有的对象都是从其他对象克隆得来的,并且可以随时修改对象的结构。
Self:是原型编程的先驱之一,它完全基于原型的概念,放弃了传统的类和实例的区分。
Lua:虽然是一种多范式的脚本语言,但它也支持原型继承。
Io:这是一种小巧的脚本语言,同样采用原型编程范式。
泛型编程
泛型编程是一种编程范式,主要在编程语言中用来实现算法和数据结构的通用性。它允许程序员编写代码以操作任意数据类型,从而使得代码更加模块化、可重用,并减少重复。
特点:
1.类型抽象:泛型编程允许开发者定义操作任意类型(Type-agnostic)的函数和数据结构,而不需要在编写时指定具体的数据类型。
2.代码复用:通过使用泛型,可以创建可应用于多种数据类型的单一实现,从而提高代码的复用率。
3.类型安全:泛型提供了一种类型安全的方式来处理不同的数据类型,可以在编译时捕获类型错误,减少运行时错误。
4.性能优化:泛型编程可以在编译时解析类型,这意味着无需运行时的类型检查,可以提高程序的执行效率。
应用:泛型编程非常适用于需要处理多种数据类型但又要保持代码简洁和安全的场景。
1.集合库:如列表、映射、集等数据结构,它们可以存储任何类型的元素。
2.算法库:可以操作各种类型的数据结构,如排序、搜索等。
3.工具和框架:在开发框架和库时使用泛型可以极大地增强其灵活性和可用性。
C++:通过模板机制支持泛型。模板允许在类和函数级别上操作泛型类型,非常灵活但有时可能导致编译时间较长。
Java:在Java中,泛型是通过使用类型参数来实现的,例如 List
C#:提供了强类型的泛型支持,不仅在集合类库中广泛使用,还能够用于自定义的数据结构和函数。
Python:泛型在Python中通常是通过鸭子类型(Duck Typing)实现的,不过最近的版本通过类型提示(Type Hints)和模块如typing,提供了更明确的泛型支持。
Rust:提供了非常强大的泛型系统,允许高度的类型安全和性能优化,通过特性(Traits)和生命周期标注进一步增强了其表达能力
事件驱动编程
事件驱动编程是一种编程范式,它强调的是在软件系统中以事件为核心来设计和处理程序流程。在事件驱动的系统中,程序的执行流程主要是由外部事件(如用户操作、系统消息或其他条件触发的事件)来决定。这种模式特别适合于处理异步或非连续交互的应用程序,例如用户界面、网络服务和任何需要响应外部活动的场景。
特点:
1.基于事件的逻辑:程序的主要逻辑围绕事件的发生和处理构建。事件可以是用户的点击、系统的通知或是时间的推移。
2.非阻塞设计:在等待事件的同时,程序可以继续执行其他任务,这是通过非阻塞操作或多线程实现的。
3.回调函数:事件处理通常通过回调函数实现,即一个事件触发时,相应的处理函数被调用。
4.解耦:事件驱动编程通常可以降低程序组件之间的耦合度,因为组件不需要知道其他组件的具体实现,只需关注事件的发布和订阅。
5.可扩展性:事件驱动的架构使得增加新的事件处理器或修改现有逻辑变得相对简单,有助于应对不断变化的需求。
应用:事件驱动编程适用于多种场景,尤其是那些需要快速响应外部事件的应用
1.用户界面:图形用户界面(GUI)应用程序广泛采用事件驱动模式来响应用户交互。
2.网络应用:服务器需要处理大量并发请求,例如HTTP请求,事件驱动模型能够有效地管理这些并发操作。
3.游戏开发:游戏程序需要响应用户的输入和内部事件,如碰撞检测和时间触发器。
4.实时系统:需要快速响应外部信号的系统,如交易系统、实时监控和自动化控制系统。
5.系统和驱动开发:进行注册和注销的的设计思路
JavaScript:在Web开发中,JavaScript是实现客户端事件驱动编程的主要语言,广泛用于处理用户界面事件如点击、滚动、键盘输入等。
Node.js:利用JavaScript的异步特性,Node.js 是一个服务器端平台,它使用事件驱动的非阻塞I/O模型,非常适合于处理大量并发连接。
C#:在.NET框架中,C# 支持事件和委托(Delegates),使得编写响应系统事件或自定义事件的程序变得简洁明了。
Java:使用监听器(Listeners)和事件对象来处理事件,常见于GUI应用程序(如Swing库)和Android开发。
cpp和c也可以利用这种思想,比如注册函数指针等。linux底层的设计和开发等
面向切面编程
面向切面编程(Aspect-Oriented Programming,简称AOP)是一种编程范式,旨在通过将横切关注点(cross-cutting concerns)从业务主体逻辑中分离出来,增强模块化。这种方法特别适用于处理那些与主要业务逻辑正交但在多处应用中重复出现的问题,如日志记录、事务管理、权限检查、异常处理等。
特点:
1.切面(Aspect):一个模块化的组件,封装了一个跨多个点的关注点(横切关注点)。
2.连接点(Join point):程序执行过程中的某个特定点,比如方法的调用或特定的字段被访问。
3.通知(Advice):在连接点上执行的动作。通知定义了在目标方法执行之前、之后或周围执行的代码。
4.切入点(Pointcut):匹配一个或多个连接点的表达式。它告诉AOP框架在哪里应用通知(即在哪些连接点执行某个通知)。
5.目标对象(Target Object):被一个或多个切面所通知的对象。
6.织入(Weaving):是将切面与其他应用类型或对象链接起来,可以在编译时、加载时或运行时进行。12.3.4.5.6..
应用:面向切面编程适用于处理各种跨应用程序多个部分的问题
1.日志记录:自动记录方法的调用,这在调试和监控中非常有用。
2.事务管理:在数据库操作前后自动开始和提交事务。
3.安全性:在方法执行前检查权限,确保只有具有适当权限的用户才能执行操作。
4.异常处理:为方法提供统一的异常处理逻辑,避免代码重复。
5.性能监控:自动记录方法的执行时间,用于性能分析。
AspectJ:是Java语言中最知名的AOP实现,提供了语言扩展来支持AOP。
Spring AOP:是Spring框架的一部分,提供了AOP支持,但它不像AspectJ那样强大,主要支持基于代理的运行时织入。
元编程
元编程是一种编程技术,它涉及编写可以操纵、生成或变形代码的程序。简单来说,元编程是“编写代码的代码”。这种方法允许开发者编写更灵活、更智能的程序,通过动态地创建或修改代码,或者在编译时或运行时改变程序的行为。
特点:
1.代码生成:编写能够生成其他代码的程序。这种方式可以用于自动化重复的编程任务,提高开发效率和减少错误。
2.反射:程序在运行时访问、检测和修改其自身结构的能力,如类、方法和属性等。这使得程序可以根据自身的状态或行为动态地做出决策。
3.宏(Macro):在编译时扩展的代码片段。宏可以在代码编译之前进行代码的插入或修改,使得可以在编译器层面改变代码的行为。
4.模板元编程:尤其在C++中,利用模板来生成编译时计算的代码。模板元编程可以用于创建编译时决定的高效代码。
优势:
1.灵活性:程序可以根据运行时的数据和条件动态地改变其行为。
2.代码重用和抽象:通过生成代码,可以避免编写重复的代码,提高抽象层级。
3.性能优化:在编译时计算复杂的表达式或决策,运行时性能得到提升。
挑战:
1.可读性和维护性:高度的动态性可能使代码难以理解和维护。
2.复杂性:错误可能难以调试,尤其是当代码在编译时或运行时生成和修改时。
3.性能开销:运行时的元编程可能引入性能开销,尤其是在使用大量反射或动态代码生成时。
元编程是一种强大的工具,适用于需要高度抽象和灵活性的场景,但它也需要谨慎使用,以避免过度复杂化代码和降低程序的可维护性。
C++:提供了强大的模板功能,允许进行高度复杂的编译时计算和类型操作。
Python:通过装饰器、元类(metaclass)和其他反射机制,支持运行时的元编程。
Lisp:其宏系统允许开发者写出可以在编译时展开和转换为其他Lisp代码的程序,非常强大和灵活。
Ruby:提供了开放类和元编程能力,允许在运行时动态地添加或改变对象的方法和属性。
JavaScript:通过原型链和高阶函数,允许在运行时修改和扩展对象和类的行为。
面向响应编程
面向响应编程(Reactive Programming)是一种编程范式,关注于异步数据流和变化的传播。这种编程模式允许程序在数据变化时自动传递这些变化,从而响应地更新相关的处理逻辑。面向响应编程特别适用于需要处理实时数据、高并发请求、大规模分布式系统以及响应用户界面的变化。
特点:
1.数据流和变化传播:在响应式编程中,数据流被视为一系列随时间推移的事件。这些数据流可以被创建、组合、转换或消费。
2.异步和非阻塞:响应式编程鼓励使用异步和非阻塞的操作,这有助于提高应用的可伸缩性和响应性。
3.观察者模式:响应式编程经常使用观察者模式,其中数据流的订阅者会在观察到数据变化时自动接收通知并做出响应。
4.函数式编程技术:响应式编程常常与函数式编程技术结合使用,利用函数式的无副作用和数据不可变性原则来处理数据流。
优点:
1.提高性能:通过非阻塞操作和异步处理,可以更高效地利用系统资源,特别是在IO密集型操作中。
2.易于处理复杂的动态系统:响应式编程可以简化动态和事件驱动系统的处理,如用户界面响应、实时数据处理等。
3.提高错误处理能力:响应式编程支持在数据流中进行错误处理,可以集中控制错误并进行适当的反应。
挑战:
1.学习曲线:响应式编程引入了许多新的概念和抽象,对于新手来说可能较难掌握。
2.调试困难:由于异步和非阻塞的特性,调试响应式编程中的问题可能比传统模式更复杂。
3.资源管理:管理异步操作和数据流需要精心设计,以避免资源泄露和性能问题。
应用场景:
1.用户界面开发:如JavaScript的Web应用,响应式编程可以用于管理DOM事件和状态变化。
2.实时数据处理:如金融市场的实时报价系统,响应式编程可以用于高效处理和传输大量动态数据。
3.网络应用:例如在Spring WebFlux中,使用响应式编程来处理和优化Web请求的处理。
常用的响应式编程工具和库:
RxJava/RxJS/Rx.NET:一系列的库,支持多种编程语言,用于实现响应式编程。
Reactor:Java的响应式编程库,常用于Spring框架。
Akka:用于构建并行、分布式和容错系统的工具集,支持Scala和Java语言
并发编程
并发编程是指在同一时间内处理多个任务的编程方法。在现代计算环境中,这通常涉及到同时执行的线程或进程,以提高程序的性能和响应性。并发编程对于充分利用多核处理器以及处理多任务操作如网络请求、数据库操作和用户界面管理等非常重要。
特点:
1.线程和进程:
进程:操作系统分配资源的基本单位,拥有独立的内存空间。
线程:操作系统调度执行的基本单位,多个线程可以共享同一个进程的资源。线程比进程更轻量,切换成本更低。
2.任务并行性:同时执行多个任务,每个任务可以是一段代码的执行流。
3.数据并行性:将数据分割,同时在多个核心或处理器上进行处理。
4.同步与异步:同步操作需要等待任务完成才能继续,而异步操作可以在等待任务完成的同时继续执行其他任务。
5.死锁、活锁和饥饿:
死锁:两个或更多的执行线程互相等待对方持有的资源,导致无法继续执行。
活锁:线程不断重试一个总是失败的操作,没有停滞,但也无法前进。
饥饿:一个或多个线程无法获得必需的资源,因而无法执行。
6.线程安全:代码在多线程环境下能够正确执行,不会因为线程调度或时间顺序的不同而产生错误。
并发编程的技术和工具:
1.锁(例如互斥锁、读写锁):用于控制对共享资源的访问,保证一次只有一个线程可以使用资源。
2.信号量:限制对资源访问的线程数量,用于实现资源的公平分配。
3.原子操作:不可被线程调度机制中断的操作,用以保证其完整性,常用于更新共享状态。
4.线程局部存储:为每个线程提供独立的变量副本,以避免共享。
5.并发集合:如Java中的ConcurrentHashMap,提供线程安全的数据结构。
6.协程:轻量级的线程,如在Python、Go和Kotlin中实现,提供更高效的任务调度。
应用场景:
1.Web服务器:同时处理多个网络请求。
2.数据库系统:处理多个查询和事务。
3.实时系统:如游戏服务器或交易系统,需要快速响应外部事件。
4.科学计算和数据分析:处理大规模数据集,利用多核性能优化计算。
常用的并发编程语言:
Java:提供了广泛的并发编程工具,包括java.util.concurrent库。
C++:支持多线程库,如C++11以后标准中的线程支持。
Python:通过线程和asyncio库支持并发,虽然受全局解释器锁(GIL)影响。
Go:内建支持并发,通过Goroutines和Channels实现。
Rust:提供数据竞争安全保证,支持无锁并发。
网络编程
网络编程是计算机编程的一个分支,它涉及编写可以在网络中进行通信的软件。这种通信可以在本地网络(如局域网)或跨越广阔的网络(如互联网)。网络编程使得不同计算机上的程序能够互相发送和接收数据,从而执行分布式计算和信息共享。
特点:
1.套接字(Sockets):
套接字是网络通信的基本构建块,它提供了程序间的通信端点。套接字可以是面向连接的(通常是TCP套接字),确保数据正确、完整地传 输;也可以是无连接的(通常是UDP套接字),传输速度快,但不保证数据的完整性或顺序。
协议:
2.TCP/IP(传输控制协议/互联网协议)是最常用的网络通信协议。TCP负责确保数据包的正确顺序和完整性,而IP处理数据包的路由。
HTTP(超文本传输协议)是用于从Web服务器传输网页的协议。
FTP(文件传输协议)、SMTP(简单邮件传输协议)等其他协议支持文件传输和电子邮件发送等功能。
3.IP地址和端口号:
IP地址用于标识网络中的设备,而端口号用于标识设备上的特定应用程序。在网络编程中,一个套接字通过IP地址和端口号的组合来标识通 信的端点。
4.客户端-服务器模型:
在这个模型中,服务器程序在网络中的某台机器上运行,等待和处理来自客户端的请求。客户端程序生成请求发送到服务器,然后接收服务器的响应。
5.非阻塞和异步编程:
在处理网络请求时,非阻塞和异步编程技术可以帮助程序继续执行其他任务,而不是等待网络操作完成,从而提高应用程序的性能和响应性。
常用的网络编程工具和库:
Berkeley套接字(BSD Sockets):几乎所有操作系统都支持的一种编程接口,用于访问底层网络服务。
Java网络编程:Java提供了java.net包,用于实现包括套接字通信在内的网络应用。
Python的socket模块:Python标准库中的socket模块简化了网络通信的实现。
.NET Framework:C#和其他.NET语言使用System.Net和System.Net.Sockets命名空间来进行网络编程。
Node.js:使用JavaScript进行网络编程,特别是异步网络应用。
应用场景:
Web应用开发:服务器和客户端(浏览器)之间的交互。
远程服务:如远程数据库访问、云服务和在线游戏服务器。
物联网(IoT):设备通过网络相互通信,进行数据收集和远程控制。
分布式计算:通过网络分布式处理大规模数据处理任务。
结语
选择不同侧重方案是有利于整体的维护和开发的,世界上不是只有面向过程和面向对象,这些都只是一些好的编程思路和编程规范,没人强制你这样编程,你甚至写汇编都没人说你。但是一个好的思路,一个正确的切入点带来的收益是巨大的,它带来的思想和理解,可以帮助你克服更大的困难。比如说侧重于多任务,那么就是使用rtos或者并发编程的那些思路去设计和开发。因为此时底层那些软件都是已经写好的了,此时想要解决的是多个任务的运行设计,所以,此时要转换编程思路才能事半功倍哦。