当前位置:首页 » 编程软件 » 面向协议编程

面向协议编程

发布时间: 2022-08-18 01:57:02

1. 面向过程编程和面向对象编程各自的优缺点

面向对象程序设计

作者:佚名 文章来源:不详 点击数:11677 更新时间:2005-10-14

作者:Ramchandra Garge

软件危机(Software Crisis)
软件技术总是处于不断发展变化中,新工具、新技术相继产生。这就要求软件产业和软件工程师们不停的寻求软件设计和开发的新途径。由于日益增长的软件系统的复杂性和软件产业内部愈演愈烈的竞争,这种要求变得更加紧迫。为了克服这种要求带来的软件危机,必须解决以下问题:
1、在系统设计中,如何表现问题的真实实体?
2、如何以开放的界面(interface)设计系统?
3、如何保证模块(mole)的可重用性(reusability)和可扩展性(extensibility)?
4、如何开发能够容忍(tolerant)未来可能的变化模块?
5、如何提高软件的生产力和减少软件开销?
6、如何管理进度表?
7、如何提高软件质量?
8、如何将软件开发过程工业化?
当软件产品在未完成时、未被使用时或者带着各种各样的错误发布时,问题就会出现。另外,用户需求的改变已经成为一个重要问题。多份关于软件实现的报告显示,在软件产品发布和使用之前,需要仔细进行质量评估。通常状态评估中应该考虑的质量因素包括:
1、正确性(Correctness)
2、可维护性(Maintainability)
3、可重用性(Reusability)
4、开放性(Openness)和可解释性(Interpretability)
5、可移植性(Portability)
6、安全性(Security)
7、完整性(Integrity)
8、用户友好性(User friendliness)

软件演化(Software Evolution)
Ernest Tello——人工智能领域的着名作家——将软件技术的演化比喻为树的生长。和树一样,软件的演化具有明显的阶段性,这些阶段称为层(layer)。过去四十年中,这些层逐步被建立起来,每一个层都由前一个层发展而成。图1显示了这个过程。但是关于树的比喻在遇到层的生命期的问题时失败了。在软件系统中,每个层都在持续的发挥作用,而在树中,只有最上层的层才有用。
面向对象程序设计(OOP)是完成程序设计工作的新方法。自从计算机发明以来,为了适应程序复杂性的不断增长,程序设计的方法有了戏剧性的变化。汇编语言被发明出来以后,程序员们总算可以用符号表示那些机器指令,从而可以编写更长、更复杂的程序。当程序规模继续不停增长的时候,高级语言被引入,为程序员们提供了更多工具对付日益增加的复杂性。第一个被普遍使用的语言是FORTRAN。不过虽然FORTRAN迈出了重大的第一步,但用它写出的代码很难说是清晰的和容易理解的。

1960年结构化程序设计思想诞生。C和Pascal等语言都大力提倡这种程序设计的方法。结构化程序设计语言使得编写较复杂的程序变得容易。但是,一旦某个项目达到一定规模,即使使用结构化程序设计的方法,局势仍将变得不可控制。
在程序设计方法发展过程中,每一次重大突破都使得程序员可以应对更大的复杂性。在这条道路上迈出的每一步中,新的方法都运用和发展了以前的方法中最好的理念。今天,许多项目的规模又进一步发展。为了解决这个问题,面向对象程序设计方法应运而生。
在详细介绍面向对象程序设计之前,让我们简单了解一下面向过程程序设计的方法。在面向过程的程序设计方法中,问题被看作一系列将被完成的任务,如读、计算和打印。许多函数用于完成这些任务。问题的焦点集中于函数。图2显示了一个典型的面向过程的程序结构。分层分解的技术被用来确定一系列需要被完成的任务,以解决特定的问题。

面向过程程序设计的基本任务是编写计算机执行的指令序列,并把这些指令以函数的方式组织起来。通常我们使用流程图组织这些行为(action),并描述从一个行为到另一个行为的控制流。
当我们集中精力开发函数的时候,很少会去注意那些被多个函数使用的数据(data)。在这些数据身上发生了什么事情?那些使用这些数据的函数又对它们产生了什么影响?
在多函数(multi-function)程序中,许多重要的数据被放置在全局数据区,这样它们可以被所有的函数访问。每个函数都可以具有它们自己的局部数据。图3显示了一个面向过程程序中函数和数据的关系。

面向对象程序设计模式
发明面向对象程序设计方法的主要出发点是弥补面向过程程序设计方法中的一些缺点。OOP把数据看作程序开发中的基本元素,并且不允许它们在系统中自由流动。它将数据和操作这些数据的函数紧密的连结在一起,并保护数据不会被外界的函数意外的改变。OOP允许我们将问题分解为一系列实体——这些实体被称为对象(object),然后围绕这些实体建立数据和函数。面向对象程序设计中的数据和函数的组织结构如图4所示。

一个对象的数据不能访问其它对象的函数,而一个对象的函数可以访问其它对象的函数。
面向对象程序设计的一些显着的特性包括:
·程序设计的重点在于数据而不是过程;
·程序被划分为所谓的对象;
·数据结构为表现对象的特性而设计;
·函数作为对某个对象数据的操作,与数据结构紧密的结合在一起;
·数据被隐藏起来,不能为外部函数访问;
·对象之间可以通过函数沟通;
·新的数据和函数可以在需要的时候轻而易举的添加进来;
·在程序设计过程中遵循由下至上(bottom-up)的设计方法。
面向对象程序设计在程序设计模式中是一个新的概念,对于不同的人可能意味着不同的内容。因此在我们继续下面的内容之前,最好给面向对象程序设计下一个定义。我们对面向对象程序设计的定义是“面向对象程序设计是一种方法,这种方法为数据和函数提供共同的独立内存空间,这些数据和函数可以作为模板以便在需要时创建类似模块的拷贝。这样的程序设计方法称为面向对象程序设计。”
从以上定义可以看到,一个对象被认为是计算机内存中的一个独立区间,在这个区间中保存着数据和能够访问数据的一组操作。因为内存区间是相互独立的,所以对象可以不经修改就应用于多个不同的程序中。

什么是面向对象程序设计?
面向对象程序设计(OOP)技术汲取了结构化程序设计中好的思想,并将这些思想与一些新的、强大的理念相结合,从而给你的程序设计工作提供了一种全新的方法。通常,在面向对象的程序设计风格中,你会将一个问题分解为一些相互关联的子集,每个子集内部都包含了相关的数据和函数。同时,你会以某种方式将这些子集分为不同等级,而一个对象就是已定义的某个类型的变量。当你定义了一个对象,你就隐含的创建了一个新的数据类型。

面向对象程序设计中的基本概念
“面向对象”作为一个术语,在不同的人群中有着不同的解释。因此,了解一些在面向对象程序设计中广泛应用的概念是必须的。本节我们讨论以下这些内容:
1、对象(Object)
2、类(Class)
3、数据抽象(Data abstraction)
4、继承(Inheritance)
5、动态绑定(Dynamic binding)
6、数据封装(Data encapsulation)
7、多态性(Polymorphism)
8、消息传递(Message passing)

对象
在一个面向对象的系统中,对象是运行期的基本实体。它可以用来表示一个人或者说一个银行帐户,一张数据表格,或者其它什么需要被程序处理的东西。它也可以用来表示用户定义的数据,例如一个向量,时间或者列表。在面向对象程序设计中,问题的分析一般以对象及对象间的自然联系为依据。如前所述,对象在内存中占有一定空间,并且具有一个与之关联的地址,就像Pascal中的record和C中的结构一样。
当一个程序运行时,对象之间通过互发消息来相互作用。例如,程序中包含一个“customer”对象和一个“account”对象,而customer对象可能会向account对象发送一个消息,查询其银行帐目。每个对象都包含数据以及操作这些数据的代码。即使不了解彼此的数据和代码的细节,对象之间依然可以相互作用,所要了解的只是对象能够接受的消息的类型,以及对象返回的响应的类型,虽然不同的人会以不同的方法实现它们。


我们刚才提到,对象包含数据以及操作这些数据的代码。一个对象所包含的所有数据和代码可以通过类来构成一个用户定义的数据类型。事实上,对象就是类类型(class type)的变量。一旦定义了一个类,我们就可以创建这个类的多个对象,每个对象与一组数据相关,而这组数据的类型在类中定义。因此,一个类就是具有相同类型的对象的抽象。例如,芒果、苹果和桔子都是fruit类的对象。类是用户定义的数据类型,但在一个程序设计语言中,它和内建的数据类型行为相同。比如创建一个类对象的语法和创建一个整数对象的语法一模一样。如果fruit被定义为一个类,那么语句
fruit mango;
就创建了一个fruit类的对象mango。

数据抽象和封装
把数据和函数包装在一个单独的单元(称为类)的行为称为封装。数据封装是类的最典型特点。数据不能被外界访问,只能被封装在同一个类中的函数访问。这些函数提供了对象数据和程序之间的接口。避免数据被程序直接访问的概念被称为“数据隐藏”。
抽象指仅表现核心的特性而不描述背景细节的行为。类使用了抽象的概念,并且被定义为一系列抽象的属性如尺寸、重量和价格,以及操作这些属性的函数。类封装了将要被创建的对象的所有核心属性。因为类使用了数据抽象的概念,所以它们被称为抽象数据类型(ADT)。

封装
封装机制将数据和代码捆绑到一起,避免了外界的干扰和不确定性。它同样允许创建对象。简单的说,一个对象就是一个封装了数据和操作这些数据的代码的逻辑实体。
在一个对象内部,某些代码和(或)某些数据可以是私有的,不能被外界访问。通过这种方式,对象对内部数据提供了不同级别的保护,以防止程序中无关的部分意外的改变或错误的使用了对象的私有部分。

继承
继承是可以让某个类型的对象获得另一个类型的对象的属性的方法。它支持按级分类的概念。例如,知更鸟属于飞鸟类,也属于鸟类。就像图5中描绘的那样,这种分类的原则是,每一个子类都具有父类的公共特性。

在OOP中,继承的概念很好的支持了代码的重用性(reusability),也就是说,我们可以向一个已经存在的类中添加新的特性,而不必改变这个类。这可以通过从这个已存在的类派生一个新类来实现。这个新的类将具有原来那个类的特性,以及新的特性。而继承机制的魅力和强大就在于它允许程序员利用已经存在的类(接近需要,而不是完全符合需要的类),并且可以以某种方式修改这个类,而不会影响其它的东西。
注意,每个子类只定义那些这个类所特有的特性。而如果没有按级分类,每类都必须显式的定义它所有的特性。

多态
多态是OOP的另一个重要概念。多态的意思是事物具有不同形式的能力。举个例子,对于不同的实例,某个操作可能会有不同的行为。这个行为依赖于所要操作数据的类型。比如说加法操作,如果操作的数据是数,它对两个数求和。如果操作的数据是字符串,则它将连接两个字符串。
图6演示了一个函数处理不同数量、不同类型的参数。就像某个单词在不同的上下文中具有不同的含义。

多态机制使具有不同内部结构的对象可以共享相同的外部接口。这意味着,虽然针对不同对象的具体操作不同,但通过一个公共的类,它们(那些操作)可以通过相同的方式予以调用。多态在实现继承的过程中被广泛应用。
面向对象程序设计语言支持多态,术语称之为“one interface multiple method(一个接口,多个实现)”。简单来说,多态机制允许通过相同的接口引发一组相关但不相同的动作,通过这种方式,可以减少代码的复杂度。在某个特定的情况下应该作出怎样的动作,这由编译器决定,而不需要程序员手工干预。
在多函数程序中,许多重要的数据被声明为全局变量,这样它们才可以被所有的函数访问。每个函数又可以具有它自己的局部变量。全局变量很容易被某个函数不经意之间改变。而在一个大程序中,很难分辨每个函数都使用了哪些变量。如果我们需要修改某个外部数据的结构,我们就要修改所有访问这个数据的函数。这很容易导致bug的产生。
在结构化程序设计中,另一个严重的缺陷是不能很好的模拟真实世界的问题。这是因为函数都是面向过程的,而不是真正的对应于问题中的各个元素。
面向过程的程序设计的一些特点如下:
·强调做(算法);
·大程序被分隔为许多小程序,这些小程序称为函数;
·大多数函数共享全局数据;
·数据开放的由一个函数流向另一个函数。函数把数据从一种形式转换为另一种形式。
采用由上至下的程序设计方法。

动态绑定
绑定指的是将一个过程调用与相应代码链接起来的行为。动态绑定的意思是,与给定的过程调用相关联的代码只有在运行期才可知。它与多态和继承的联系极为紧密。一个多态引用的函数调用决定于这个引用的动态类型。
考虑图6中的“draw”方法。通过继承,每个对象都具备了这个过程。但是,对于不同的对象它的算法是不同的,因此,draw过程必须在每一个类中重新定义。在运行期,当前引用对象所对应的代码将被调用。

消息传递
一个面向对象的程序由许多对象组成,这些对象之间需要相互沟通。因此,在面向对象程序设计语言中,程序设计的主要步骤如下:
1、创建类,这些类定义了对象及其行为;
2、由类定义创建对象;
3、建立对象之间的通讯。
对象之间通过收发信息相互沟通,这一点类似于人与人之间的信息传递。信息传递的概念使得真实世界的直接模拟更易于和建立系统交流。
对于某个特定对象来说,消息就是请求执行某个过程,因此,消息的接收对象会调用一个函数(过程),以产生预期的结果。传递的消息的内容包括接收消息的对象的名字,需要调用的函数的名字,以及必要的信息。
对象就有一个生命周期。它们可以被创建和销毁。只要对象正处于其生存期,就可以与其进行通讯。

OOP的优点
OOP具有许多优点,无论是对于程序设计者或者用户来说都是如此。面向对象为软件产品扩展和质量保证中的许多问题提供了解决办法。这项技术能够大大提高程序员的生产力,并可提高软件的质量以及降低其维护费用。其主要的优点陈列于下:
1、通过继承,我们可以大幅减少多余的代码,并扩展现有代码的用途;
2、我们可以在标准的模块上(这里所谓的“标准”指程序员之间彼此达成的协议)构建我们的程序,而不必一切从头开始。这可以减少软件开发时间并提高生产效率;
3、数据隐藏的概念帮助程序员们保护程序免受外部代码的侵袭;
4、允许一个对象的多个实例同时存在,而且彼此之间不会相互干扰;
5、允许将问题空间中的对象直接映射到程序中;
6、基于对象的工程可以很容易的分割为独立的部分;
7、以数据为中心的设计方法允许我们抓住可实现模型的更多细节;
8、面向对象的系统很容易从小到大逐步升级;
9、对象间通讯所使用的消息传递技术与外部系统接口部分的描述更简单;
10、更便于控制软件复杂度。
当需要将以上所说的所有特性有机的结合于一个面向对象系统中,它们之间的相对重要性就取决于工程的类型和程序员的喜好。为了获得上述的某些优势,必须考虑很多事情。例如,对象库必须可以被重用。技术还在不停的发展,现有的产品也会很快的更新换代。如果重用没有能够实现,那么就需要进行严格的控制和管理。
易于使用的开发软件往往难以编写。面向对象程序设计工具有望解决这个问题。

面向对象程序设计语言
面向对象技术并不是某个特定语言的特权。如同结构化程序设计一样,OOP概念可以在很多语言比如C和Pascal中实现。但是,当程序越来越大时,程序设计工作会变得拙劣而混乱。而一个支持OOP概念的程序设计语言则可以让一切变得简单。
一个语言必须支持几个主要的OOP概念才能称其是面向对象的。根据所支持的OOP特性,语言可以分为以下两类:
1、基于对象的程序设计语言;
2、面向对象的程序设计语言。
基于对象的程序设计语言仅支持封装和对象辨识。
一个面向对象的程序设计语言所要支持的重要特性如下:
·数据封装
·数据隐藏和访问机制
·对象的自动初始化和清除
·操作符重载
支持对象风格程序设计的语言称为基于对象的程序设计语言。它们不支持继承和动态绑定。
Ada就是一个典型的基于对象的程序设计语言。
面向对象的程序设计不仅仅包含基于对象程序设计的特性,还支持继承和动态绑定。
OOP的应用
OOP最有前途的应用领域如下:
1、实时系统;
2、仿真和建模;
3、面相对象数据库
4、超文本、超媒体和扩展文本;
5、AI和专家系统;
6、神经网络和并行程序设计;
7、决策支持和办公自动化系统;
8、CIM/CAM/CAD系统。

2. Swift中结构体和类的区别

两者之间的关系
定义:结构体有0个或多个相同或者不同的数据组合而成的数据集合,其中那些数据或者方法被称为结构体的成员或者是成员方法。
格式:
struct name:<:protocal>
{
var 成员1:数据类型
var 成员2:数据类型
….
}
要注意的地方:
结构体是值类型,其 实例将会被赋予变量或者常量和被函数调用时被复制
结构体中的成员可以包括属性、类型别名、数组、其他结构体和枚举声明
结构体声明不能包含析构器或者协议声明,但是可以包括任意协议的实现,不能继承类、枚举、其他结构体
两者间的区别
在我们实际的开发中,可能会纠结于应该是使用类还是结构体,在这里我也总结了一下类与结构体间的一些区别。
结构体:
///MARK: - 结构体
struct Car
{
var name: String!
init(name: String){
self.name = name
}
}
var p1 = Car(name: "bench")
var p2 = p1
p2.name = "BMW"
print(p2.name,p1.name)
/// 打印结果为BMW,bench
类:
///MARK: - 类
class Car
{
var name: String!
init(name: String){
self.name = name
}
}
var p1 = Car(name: "bench")
var p2 = p1
p2.name = "BMW"
print(p1.name,p2.name,p1.name)
/// 打印结果为BMW,BMW,BMW
从上面的结果可以看出来,再次给对象赋值,结构体不会改变,而类则会改变原来的值,所以说明类的对象是引用类型,而结构体是值类型。
还有的区别就是类是属于面向对象编程,结构体属于面向协议的编程,所谓面向协议编程其实就是面向对象的升级。在swift中推荐使用的是使用结构体,类在swift中不处于主流的地位,还有就是结构体也能够实现类的全部功能,结构体更模块化,默认实现初始化方法并且不用考虑ARC。
结构体只需要给出变量的类型,不用给出初始值
struct car {
var name: String
}
类则需要给出初始值
class car {
var name: String
init(name:String) {
self.name = name
}
}
上面两种的效果一样。

3. swift 是面向对象编程还是面向过程,还是其他编程

Swift有面向过程,也有面向对象,也有面向协议。
其实很多语言都是这样的

4. 人员应该具备的编程语言有哪些

程序员的话大部分的编程语言都要了解一点的,这个没有最好,还是要看你要做些什么,不同的编程语言适合做不同的事。
C++语言,适合在校大学生、自学的转行者、喜欢计算机的人群,学习目标是C++基本语法、数据结构、STL、线程、协议编程、数据库、Socket。学后可以使用C++实现最新的项目案例,雷霆战机、远程控制、视频会议、服务器架构等。
1.C++语言:是C语言的继承,它既可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计,还可以进行以继承和多态为特点的面向对象的程序设计。
2.特点:C++不仅拥有计算机高效运行的实用性特征,同时还致力于提高大规模程序的编程质量与程序设计语言的问题描述能力。
3.基本内容:类、封装、重载、继承、模版。
希望可以帮到您,谢谢!

5. swift1.0 中append在swift2.0中用什么替代

guard语句
guard语句和if语句有点类似,都是根据其关键字之后的表达式的布尔值决定下一步执行什么。但与if语句不同的是,guard语句只会有一个代码块,不像if语句可以if else多个代码块。
那么guard语句的作用到底是什么呢?顾名思义,就是守护。guard语句判断其后的表达式布尔值为false时,才会执行之后代码块里的代码,如果为true,则跳过整个guard语句,我们举例来看看。
我们以今年高考为例,在进入考场时一般都会检查身份证和准考证,我们写这样一个方法:
func checkup(person: [String: String!]) {

// 检查身份证,如果身份证没带,则不能进入考场
guard let id = person["id"] else {
print("没有身份证,不能进入考场!")
return
}

// 检查准考证,如果准考证没带,则不能进入考场
guard let examNumber = person["examNumber"] else {
print("没有准考证,不能进入考场!")
return
}

// 身份证和准考证齐全,方可进入考场
print("您的身份证号为:\(id),准考证号为:\(examNumber)。请进入考场!")

}
checkup(["id": "123456"]) // 没有准考证,不能进入考场!
checkup(["examNumber": "654321"]) // 没有身份证,不能进入考场!
checkup(["id": "123456", "examNumber": "654321"]) // 您的身份证号为:123456,准考证号为:654321。请进入考场!

上述代码中的第一个guard语句用于检查身份证,如果检查到身份证没带,也就是表达式为false时,执行大括号里的代码,并返回。第二个guard语句则检查准考证。
如果两证齐全,则执行最后一个打印语句,上面的两个guard语句大括号内的代码都不会执行,因为他们表达式的布尔值都是true。
这里值得注意的是,id和examNumber可以在guard语句之外使用,也就是说当guard对其表达式进行验证后,id和examNumber可在整个方法的作用域中使用,并且是解包后的。
我们再用if else语句写一个类似的方法:
func checkupUseIf(person: [String: String!]) {

if let id = person["id"], let examNumber = person["examNumber"] {
print("您的身份证号为:\(id),准考证号为:\(examNumber)。请进入考场!")
} else {
print("证件不齐全,不能进入考场!")
}

print("您的身份证号为:\(id),准考证号为:\(examNumber)") // 报异常

}
checkupUseIf(["id": "123456"]) // 证件不齐全,不能进入考场!
checkupUseIf(["examNumber": "654321"]) // 证件不齐全,不能进入考场!
checkupUseIf(["id": "123456", "examNumber": "654321"]) // 您的身份证号为:123456,准考证号为:654321。请进入考场!

我们可以看到用if else实现的方法显然不如guard实现的那么精准。而且id和examNumber的作用域只限在if的第一个大括号内,超出这个作用域编译就会报错。
通过上述两个小例子不难看出,guard语句正如一个称职的守卫,层层把关,严防一切不允许发生的事,并且让代码具有更高的可读性,非常棒。
异常处理
在Swift 1.0时代是没有异常处理和抛出机制的,如果要处理异常,要么使用if else语句或switch语句判断处理,要么使用闭包形式的回调函数处理,再要么就使用NSError处理。以上这些方法都不能像Java中的try catch异常控制语句那样行如流水、从容不迫的处理异常,而且也会降低代码的可读性。当Swift 2.0到来后,一切都不一样了。
在Swift 2.0中Apple提供了使用throws、throw、try、do、catch这五个关键字组成的异常控制处理机制。下面我们来举例看看如何使用,我用使用手机刷朋友圈为例。
首先我们需要定义异常枚举,在Swift 2.0中Apple提供了ErrorType协议需要我们自定义的异常枚举遵循:
enum WechatError: ErrorType {
case NoBattery // 手机没电
case NoNetwork // 手机没网
case NoDataStream // 手机没有流量
}

我们定义了导致不能刷微信的错误枚举’wechatError。然后定义一个检查是否可以刷微信的方法checkIsWechatOk():
func checkIsWechatOk(isPhoneHasBattery: Bool, isPhoneHasNetwork: Bool, dataStream: Int) throws {

guard isPhoneHasBattery else {
throw WechatError.NoBattery
}

guard isPhoneHasNetwork else {
throw WechatError.NoNetwork
}

guard dataStream > 50 else {
throw WechatError.NoDataStream
}

}

这里注意,在方法名后有throws关键字,意思为该方法产生的异常向上层抛出。在方法体内使用guard语句对各种状态进行判断,然后使用throw关键字抛出对应的异常。然后我们定义刷微信的方法:
func playWechat(isPhoneHasBattery: Bool, isPhoneHasNetwork: Bool, dataStream: Int) {

do {
try checkIsWechatOk(isPhoneHasBattery, isPhoneHasNetwork: isPhoneHasNetwork, dataStream: dataStream)
print("放心刷,刷到天昏地暗!")
} catch WechatError.NoBattery {
print("手机都没电,刷个鬼啊!")
} catch WechatError.NoNetwork {
print("没有网络哎,洗洗玩单机吧!")
} catch WechatError.NoDataStream {
print("没有流量了,去蹭Wifi吧!")
} catch {
print("见鬼了!")
}

}
playWechat(true, isPhoneHasNetwork: true, dataStream: 60) // 放心刷,刷到天昏地暗!
playWechat(true, isPhoneHasNetwork: false, dataStream: 60) // 没有网络哎,洗洗玩单机吧!
playWechat(false, isPhoneHasNetwork: true, dataStream: 60) // 手机都没电,刷个鬼啊!
playWechat(true, isPhoneHasNetwork: true, dataStream: 30) // 没有流量了,去蹭Wifi吧!

上述的代码示例中,首先检查是否可以刷微信的方法前使用try关键字,表示允许该方法抛出异常,然后使用了do catch控制语句捕获抛出的异常,进而做相关的逻辑处理。
这套异常处理机制使Swift更加的全面和安全,并且提高了代码的可读性,非常棒。
协议扩展
在Swift 1.0 时代,协议(Protocol)基本上类似一个接口,定义若干属性和方法,供类、结构体、枚举遵循和实现。在Swift 2.0中,可以对协议进行属性或者方法的扩展,和扩展类与结构体类似。这让我们开启了面向协议编程的篇章。
Swift中,大多数基础对象都遵循了CustomStringConvertible协议,比如Array、Dictionary(Swift 1.0中的Printable协议),该协议定义了description方法,用于print方法打印对象。现在我们对该协议扩展一个方法,让其打印出大写的内容:
var arr = ["hello", "world"]
print(arr.description) // "[hello, world]"
extension CustomStringConvertible {
var upperDescription: String {
return "\(self.description.uppercaseString)"
}
}
print(arr.upperDescription) // "[HELLO, WORLD]"

如果在Swfit 1.0时代,要想达到上述示例的效果,那么我们需要分别对Array、Dictionary进行扩展,所以协议的扩展极大的提高了我们的编程效率,也同样使代码更简洁和易读。
打印语句的改变
在Swift1中,有'println()'和'print()'两个在控制台打印语句的方法,前者是换行打印,后者是连行打印。在Swift2中,'println()'已成为过去,取而代之的是他俩的结合体。如果你想做换行打印,现在需要这样写:
print("我要换行!", appendNewline: true)

available检查
作为iOS开发者,谁都希望使用最新版本iOS的Api进行开发,省事省力。但常常事与愿违,因为我们经常需要适配老版本的iOS,这就会面临一个问题,一些新特性特性或一些类无法在老版本的iOS中使用,所以在编码过程中经常会对iOS的版本做以判断,就像这样:
if NSClassFromString("NSURLQueryItem") != nil {
// iOS 8或更高版本
} else{
// iOS8之前的版本
}

以上这只是一种方式,在Swift 2.0之前也没有一个标准的模式或机制帮助开发者判断iOS版本,而且容易出现疏漏。在Swift 2.0到来后,我们有了标准的方式来做这个工作:
if #available(iOS 8, *) {
// iOS 8或更高版本
let queryItem = NSURLQueryItem()

} else {
// iOS8之前的版本

}

这个特性让我们太幸福。
do-while语句重命名
经典的do-while语句改名了,改为了repeat-while:
var i = 0
repeat {
i++
print(i)
} while i < 10

个人感觉更加直观了。
defer关键字
在一些语言中,有try/finally这样的控制语句,比如Java。这种语句可以让我们在finally代码块中执行必须要执行的代码,不管之前怎样的兴风作浪。在Swift 2.0中,Apple提供了defer关键字,让我们可以实现同样的效果。
func checkSomething() {

print("CheckPoint 1")
doSomething()
print("CheckPoint 4")

}
func doSomething() {

print("CheckPoint 2")
defer {
print("Clean up here")
}
print("CheckPoint 3")

}
checkSomething() // CheckPoint 1, CheckPoint 2, CheckPoint 3, Clean up here, CheckPoint 4

上述示例可以看到,在打印出“CheckPoint 2”之后并没有打印出“Clean up here”,而是“CheckPoint 3”,这就是defer的作用,它对进行了print("Clean up here")延迟。我们再来看一个I/O的示例:
// 伪代码
func writeSomething() {

let file = OpenFile()

let ioStatus = fetchIOStatus()
guard ioStatus != "error" else {
return
}
file.write()

closeFile(file)

}

上述示例是一个I/O操作的伪代码,如果获取到的ioStatus正常,那么该方法没有问题,如果ioStatus取到的是error,那么会被guard语句抓到执行return操作,这样的话closeFile(file)就永远都不会执行了,一个严重的Bug就这样产生了。下面我们看看如何用defer来解决这个问题:
// 伪代码
func writeSomething() {

let file = OpenFile()
defer {
closeFile(file)
}

let ioStatus = fetchIOStatus()
guard ioStatus != "error" else {
return
}
file.write()

}

我们将closeFile(file)放在defer代码块里,这样即使ioStatus为error,在执行return前会先执行defer里的代码,这样就保证了不管发生什么,最后都会将文件关闭。
defer又一个保证我们代码健壮性的特性,我非常喜欢。
Swift 2.0中的新特性当然不止以上这些,但窥一斑可见全豹,Swift 2.0努力将更快、更安全做到极致,这是开发人员的福音,让我们尽情享受这门美妙的语言吧。

6. 面向连接和无连接方式套接字编程有什么不同

1、关于使用套接字编程的一些基本概念
二元组的定义:<K,R>
三元组的定义:<D,F,A>
五元组的定义:<V,O,G,M,S>
V是值的集合,O是操作的集合,G是构成名字的文法,M是存储的集合,S是从G能构成的名字 几个到M的映射.

(a)半相关与全相关
半相关:在网络中用一个三元组可以在全局唯一标志一个进程: (协议,本地地址,本地端口号)这样一个三元组,叫做一个半相关(half-association),它指定连接的每半部分。
全相关:一个完整的网间进程通信需要由两个进程组成,并且只能使用同一种高层协议。也就是说,不可能通信的一端用TCP协议,而另一端用UDP协议。因此一个完整的网间通信需要一个五元组来标识:(协议,本地地址,本地端口号,远地地址,远地端口号)这样一个五元组,叫做一个相关(association),即两个协议相同的半相关才能组合成一个合适的相关,或完全指定组成一连接。

(b)TCP/IP协议的地址结构为:
struct sockaddr_in
{
short sin_family; /*AF_INET*/
u_short sin_port; /*16位端口号,网络字节顺序*/
struct in_addr sin_addr; /*32位IP地址,网络字节顺序*/
char sin_zero[8]; /*保留*/
}

(c)套接字类型
TCP/IP的socket提供下列三种类型套接字。

流式套接字(SOCK_STREAM):提供了一个面向连接、可靠的数据传输服务,数据无差错、无重复地发送,且按发送顺序接收。内设流量控制,避免数据流超限;数据被看作是字节流,无长度限制。文件传送协议(FTP)即使用流式套接字。

数据报式套接字(SOCK_DGRAM):提供了一个无连接服务。数据包以独立包形式被发送,不提供无错保证,数据可能丢失或重复,并且接收顺序混乱。网络文件系统(NFS)使用数据报式套接字。

原始式套接字(SOCK_RAW):该接口允许对较低层协议,如IP、ICMP直接访问。常用于检验新的协议实现或访问现有服务中配置的新设备。

(d)基本套接字系统调用
为了更好地说明套接字 编程原理,下面给出几个基本套接字系统调用说明。
(1)创建套接字──socket()
应用程序在使用套接字前,首先必须拥有一个套接字,系统调用socket()向应用程序提供创建套接字的手段,其调用格式如下:

SOCKET socket(int af, int type, int protocol);

该调用要接收三个参数:af、type、protocol。参数af指定通信发生的区域,UNIX系统支持的地址族有:AF_UNIX、AF_INET、AF_NS等,而DOS、WINDOWS中仅支持AF_INET,它是网际网区域。因此,地址族与协议族相同。参数type描述要建立的套接字的类型。参数protocol 说明该套接字使用的特定协议,如果调用者不希望特别指定使用的协议,则置为0,使用默认的连接模式。根据这三个参数建立一个套接字,并将相应的资源分配给它, 同时返回一个整型套接字号。因此,socket()系统调用实际上指定了相关五元组中的“协议”这一元。

(2)指定本地地址──bind()
当一个套接字用socket()创建后,存在一个名字空间(地址族),但它没有被命名。bind()将套接字地址(包括本地主机地址和本地端口地址)与所创建的套接字号联系起来,即将名字赋予套接字,以指定本地半相关。其调用格式如下:

int bind(SOCKET s, const struct sockaddr FAR * name, int namelen);

参数 s 是由 socket() 调用返回的并且未作连接的套接字描述符(套接字号)。参数name是赋给套接字s的本地地址(名字),其长度可变,结构随通信域的不同而不同。namelen表明了name的长度。 如果没有错误发生,bind()返回0。否则返回值SOCKET_ERROR。 地址在建立套接字通信过程中起着重要作用,作为一个网络应用程序设计者对套接字地址结构必须有明确认识。

(3)建立套接字连接──connect()与accept()
这两个系统调用用于完成一个完整相关的建立,其中connect()用于建立连接。无连接的套接字进程也可以调用connect(),但这时在进程之间没有实际的报文交换,调用将从本地操作系统直接返回。这样做的优点是程序员不必为每一数据指定目的地址,而且如果收到的一个数据报,其目的端口未与任何套接字建立“连接”,便能判断该端口不可操作。而accept()用于使服务器等待来自某客户进程的实际连接。 connect()的调用格式如下:

int connect(SOCKET s,const struct sockaddr FAR * name,int namelen);

参数s是欲建立连接的本地套接字描述符。参数name指出说明对方套接字地址结构的指针。对方套接字地址长度由namelen说明。 如果没有错误发生,connect()返回0。否则返回值SOCKET_ERROR。在面向连接的协议中,该调用导致本地系统和外部系统之间连接实际建立。 由于地址族总被包含在套接字地址结构的前两个字节中,并通过socket()调用与某个协议族相关。因此bind()和connect()无须协议作为参数。 accept()的调用格式如下:

SOCKET accept(SOCKET s,struct sockaddr FAR* addr,int FAR* addrlen);

参数s为本地套接字描述符,在用做accept() 调用的参数前应该先调用过listen()。addr 指向客户方套接字地址结构的指针, 用来接收连接实体的地址。addr的确切格式由套接字创建时建立的地址族决定。addrlen 为客户方套接字地址的长度(字节数)。如果没有错误发生,accept()返回一个SOCKET类型的值,表示接收到的套接字的描述符。否则返回值INVALID_SOCKET。 accept()用于面向连接服务器。参数addr和addrlen 存放客户方的地址信息。调用前,参数addr 指向一个初始值为空的地址结构,而 addrlen 的初始值为0; 调用accept() 后,服务器等待从编号为s的套接字上接受客户连接请求,而连接请求是由客户方的connect()调用发出的。当有连接请求到达时,accept()调用将请求连接队列上的第一个客户方套接字地址及长度放入addr和addrlen,并创建一个与s有相同特性的新套接字号。新的套接字可用于处理服务器并发请求。

四个套接字系统调用,socket()、bind()、connect()、accept(),可以完成一个完全五元相关的建立。socket()指定五元组中的协议元,它的用法与是否为客户或服务器、是否面向连接无关。bind()指定五元组中的本地二元,即本地主机地址和端口号,其用法与是否面向连接有关:在服务器方,无论是否面向连接,均要调用 bind() ;在客户方,若采用面向连接,则可以不调用bind(),而通过connect()自动完成。若采用无连接,客户方必须使用bind()以获得一个唯一的地址。 以上讨论仅对客户/服务器模式而言,实际上套接字的使用是非常灵活的,唯一需遵循的原则是进程通信之前,必须建立完整的相关。

(4)监听连接──listen()
此调用用于面向连接服务器,表明它愿意接收连接。listen()需在accept()之前调用,其调用格式如下:

int listen(SOCKET s, int backlog);

参数s标识一个本地已建立、尚未连接的套接字号, 服务器愿意从它上面接收请求。 backlog 表示请求连接队列的最大长度, 用于限制排队请求的个数,目前允许的最大值为5。如果没有错误发生,listen()返回0。否则它返回SOCKET_ERROR。 listen()在执行调用过程中可为没有调用过bind() 的套接字s完成所必须的连接,并建立长度为backlog的请求连接队列。 调用listen()是服务器接收一个连接请求的四个步骤中的第三步。它在调用socket() 分配一个流套接字,且调用bind()给s赋于一个名字之后调用,而且一定要在accept()之前调用。

(5)数据传输──send()与recv()
当一个连接建立以后,就可以传输数据了。常用的系统调用有 send() 和recv()。 send() 调用用于在参数s指定的已连接的数据报或流套接字上发送输出数据,格式如下:

int send(SOCKET s, const char FAR *buf, int len, int flags);

参数s为已连接的本地套接字描述符。buf 指向存有发送数据的缓冲区的指针,其长度由 len 指定。flags 指定传输控制方式,如是否发送带外数据等。如果没有错误发生,send()返回总共发送的字节数。否则它返回SOCKET_ERROR。 recv()调用用于在参数s指定的已连接的数据报或流套接字上接收输入数据,格式如下:

int recv(SOCKET s, char FAR *buf, int len, int flags);

参数s 为已连接的套接字描述符。buf指向接收输入数据缓冲区的指针,其长度由len 指定。flags 指定传输控制方式,如是否接收带外数据等。如果没有错误发生,recv()返回总共接收的字节数。如果连接被关闭,返回0。否则它返回SOCKET_ERROR。

(6)输入/输出多路复用──select()
select()调用用来检测一个或多个套接字的状态。对每一个套接字来说,这个调用可以请求读、写或错误状态方面的信息。请求给定状态的套接字集合由一个fd_set结构指示。在返回时,此结构被更新,以反映那些满足特定条件的套接字的子集,同时, select()调用返回满足条件的套接字的数目,其调用格式如下:

int select(int nfds, fd_set FAR * readfds, fd_set FAR * writefds,fd_set FAR * exceptfds, const struct timeval FAR * timeout);

参数nfds指明被检查的套接字描述符的值域,此变量一般被忽略。 参数readfds指向要做读检测的套接字描述符集合的指针,调用者希望从中读取数据。 参数 writefds 指向要做写检测的套接字描述符集合的指针。exceptfds指向要检测是否出错的套接字描述符集合的指针。timeout指向select()函数等待的最大时间,如果设为NULL则为阻塞操作。select()返回包含在fd_set结构中已准备好的套接字描述符的总数目,或者是发生错误则返回SOCKET_ERROR。

(7)关闭套接字──closesocket()

7. 面向接口编程的基本信息

在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,各个对象内部是如何实现自己的,对系统设计人员来讲就不那么重要了;而各个对象之间的协作关系则成为系统设计的关键。小到不同类之间的通信,大到各模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容。面向接口编程就是指按照这种思想来编程。
1.关于接口的理解。
接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离。
接口的本身反映了系统设计人员对系统的抽象理解。
接口应有两类:第一类是对一个体的抽象,它可对应为一个抽象体(abstract class);
第二类是对一个体某一方面的抽象,即形成一个抽象面(interface);
一个体有可能有多个抽象面。
抽象体与抽象面是有区别的。
2.设计接口的另一个不可忽视的因素是接口所处的环境(context,environment),系统论的观点:环境是系统要素所处的空间与外部影响因素的总和。任何接口都是在一定的环境中产生的。因此环境的定义及环境的变化对接口的影响是不容忽视的,脱离原先的环境,所有的接口将失去原有的意义。
3.按照组件的开发模型(3C),它们三者相辅相成,各司一面,浑然一体,缺一不可。
面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性及方法
面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现
接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题
UML里面所说的interface是协议的另一种说法。并不是指com的interface,CORBA的interface,Java的interface,Delphi的interface,人机界面的interface或NIC的interface。
在具体实现中,是可以把UML的interface实现为语言的interface,分布式对象环境的interface或其它什么interface,但就理解UML的interface而言,指的是系统每部分的实现和实现之间,通过interface所确定的协议来共同工作。
面向interface编程,原意是指面向抽象协议编程,实现者在实现时要严格按协议来办。面向对象编程是指面向抽象和具象。抽象和具象是矛盾的统一体,不可能只有抽象没有具象。一般懂得抽象的人都明白这个道理。 但有的人只知具象却不知抽象为何物。 所以只有interface没有实现,或只有实现而没有interface者是没有用的,反OO的。
所以还是老老实实面向对象编程,面向协议编程,或者什么都不面向,老老实实编程。
但是我很讨厌讨论这样的术语,不如我们谈谈什么叫面向领导的编程?面向用户的编程?领导和用户有时都很BT,我们就面向BT编程?
选择Java接口还是抽象类
很多人有过这样的疑问:为什么有的地方必须使用接口而不是抽象类,而在另一些地方,又必须使用抽象类而不是接口呢?或者说,在考虑Java类的一般化问题时,很多人会在接口和抽象类之间犹豫不决,甚至随便选择一种。
实际上接口和抽象类的选择不是随心所欲的。要理解接口和抽象类的选择原则,有两个概念很重要:对象的行为和对象的实现。如果一个实体可以有多种实现方式,则在设计实体行为的描述方式时,应当达到这样一个目标:在使用实体的时候,无需详细了解实体行为的实现方式。也就是说,要把对象的行为和对象的实现分离开来。既然Java的接口和抽象类都可以定义不提供具体实现的方法,在分离对象的行为和对象的实现时,到底应该使用接口还是使用抽象类呢?
通过抽象类建立行为模型
在接口和抽象类的选择上,必须遵守这样一个原则:行为模型应该总是通过接口而不是抽象类定义。为了说明其原因,下面试着通过抽象类建立行为模型,看看会出现什么问题。
假设要为销售部门设计一个软件,这个软件包含一个“发动机”(Motor)实体。显然无法在发动机对象中详细地描述发动机的方方面面,只能描述某些对当前软件来说重要的特征。至于发动机的哪些特征是重要的,则要与用户(销售部门)交流才能确定。
销售部门的人要求每一个发动机都有一个称为马力的参数。对于他们来说,这是惟一值得关心的参数。基于这一判断,可以把发动机的行为定义为以下行为。
行为1:查询发动机的马力,发动机将返回一个表示马力的整数。
虽然还不清楚发动机如何取得马力这个参数,但可以肯定发动机一定支持这个行为,而且这是所有发动机惟一值得关注的行为特征。这个行为特征既可以用接口定义,也可以用抽象类定义。为了说明用抽象类定义可能出现的问题,下面用抽象类建立发动机的行为模型,并用Java方法描述行为1,代码如下:
代码
public abstract Motor{
abstract public int getHorsepower();
}
在Motor抽象类的基础上构造出多种具体实现,例如A型发动机、B型发动机等,再加上系统的其它部分,最后得到1.0版的软件并交付使用。一段时间过去了,要设计2.0版的软件。在评估2.0版软件需求的过程中,发现一小部分发动机是电池驱动的,而电池需要一定的充电时间。销售部门的人希望能够通过计算机查阅充电时间。根据这一要求定义一个新的行为,如图1所示。
行为2:查询电驱动发动机的充电时间,发动机将返回一个表示充电时间的整数。
用Java方法来描述这个行为,代码如下:
代码
public abstract BatteryPoweredMotor extends Motor{
abstract public int getTimeToRecharge();
}
在销售部门的软件中,电驱动发动机也以类的形式实现,但这些类从BatteryPoweredMotor而不是Motor派生。这些改动加入到2.0版软件之后,销售部门很满意。随着业务的不断发展,不久之后光驱动的发动机出现了。销售部门要求光驱动发动机需要一定光能才能运转,光能以流明(Lumen)度量。这个信息对客户很重要,因为下雨或多云的天气里,某些光驱动发动机可能无法运转。销售部门要求为软件增加对光驱动发动机的支持,所以要定义一个新的行为。
行为3:查询光驱动发动机能够正常运转所需要的最小流明数,发动机返回一个整数。
再定义一个抽象类并把行为3转换成Java方法,代码如下:
代码
public abstract SolarPoweredMotor extends Motor{
abstract public int getLumensToOperate();

热点内容
怎么才能知道电视是不是安卓系统 发布:2025-02-07 03:04:23 浏览:815
银行更改密码红色预警是什么意思 发布:2025-02-07 02:54:22 浏览:551
androiddomain 发布:2025-02-07 02:46:04 浏览:843
端口扫描源码 发布:2025-02-07 02:31:01 浏览:470
androidurl获取图片 发布:2025-02-07 02:22:11 浏览:482
调用上传 发布:2025-02-07 02:19:53 浏览:84
aix编译安装 发布:2025-02-07 02:19:52 浏览:906
android界面设计尺寸 发布:2025-02-07 02:16:25 浏览:898
zenly安卓为什么会一直闪 发布:2025-02-07 02:12:02 浏览:358
为什么安卓手机界面总出广告 发布:2025-02-07 02:10:33 浏览:244