面向对象
理解面向对象编程
-
封装
- 对象的行为外部世界是不可见的,或者说对象的状态信息是私密的
- 客户端不能通过直接操作改变对象的内部状态。相反,客户端需要发送消息请求对象来改变内部状态,对象可以通过特定的请求类型,通过特定的成员函数(set或者get)来改变他们的内部状态,以做出相应的响应。
- Python封装(数据和方法的隐藏)的概念不是隐式的。__是一种防君子不防小人的使其可访问性变为私有的行为
-
继承
- 继承表示一个类可以继承父类的(大部分)功能
- 继承被描述为一个重用基类中定义的功能并允许对原始软件的实现独立拓展的选项
- 继承可以利用不同类的对象之间的关系建立层次结构,Python支持多继承。
-
多态
- 多态有两种类型。
- 对象根据输入参数提供方法的不同实现
- 不同类型的对象可以使用相同的接口
- 多态有两种类型。
-
抽象
- 它提供了一个简单的客户端接口,客户端可以通过该接口和类的对象进行交互,并可以调用该接口中定义的各个方法。
- 它将内部类的复杂性抽象为一个接口,这样客户端就不知道内部怎么实现
-
组合
- 组合是一种将对象或类组合成更复杂的数据结构或软件实现的方法
- 在组合中,一个对象可调用其他模块的成员函数,这样一来,无需通过继承就可以实现基本功能的跨模块使用
面向对象的设计原则
-
开放/封闭原则
类或对象及其方法对于扩展来说,是开放的,但是对于修改来说,应该是封闭的。
- 现有的类不会被修改,因而退化的可能性较小
- 有助于保持以前代码的向后兼容性
-
控制反转原则
高层级的模块不应依赖于低层次的模块,它们应该都依赖于抽象。细节应该依赖于抽象,而不是抽象依赖于细节
- 削弱了模块之间的紧耦合,因而消除了系统中的复杂性/刚性
- 由于依赖模块之间存在一个明显的抽象层(由钩子或参数提供),因而便于通过更好的方式处理模块之间的依赖关系
-
接口隔离原则
客户端不应该依赖于它们不需要的借口
- 它强制开发人员编写瘦身型接口,并使方法与接口紧密相关
- 防止向接口中随意添加方法
-
单一职责原则
类的职责单一,引起类变化的原因单一
- 每当一个功能发生变化时,除了特定的类需要改变外,其他类无需变动
- 此外,如果一个类有多个功能,那么依赖它的类必定会由于多种原因而经历多次修改,这是应该避免的
-
替换原则
派生类必须能够完全取代基类
设计模式的概念
设计模式就是解决特定问题的解决方案。
- 它们是与语言无关的,可以用多种语言实现
- 它们是动态的,随时会有新的模式引入
- 它们可以进行定制,对开发人员很有用
设计模式的优点
- 它们可以再多个项目中重复使用
- 问题可以在架构级别得到解决
- 它们都经过了时间的验证和良好的证明,是开发人员和架构师的宝贵经验
- 它们具有可靠性和依赖性
设计模式的分类
-
代码段:用某种语言编写的一段具有特定用途的代码,例如,它们可以是python的DB连接代码
-
设计:用来解决某个特定问题的优秀的解决方案
-
标准:这是一个解决某类问题的方法,它非常通用,并且适用于当前的情况
-
模式:这是一个经过时间考验的、高效、可扩展的解决方案,能够解决一类已知问题
上下文-设计模式的适用性
- 参与者:它们是在设计模式中用到的类,类可以在模式中扮演不同的角色,已完成多个目标
- 非功能需求:诸如内存优化,可用性和性能等需求都属于此类型。由于这些因素影响整个软件解决方案,因此至关重要。
- 权衡:并非所有的设计模式都适用于应用程序开发,因此需要权衡,这些是在应用程序中使用设计模式时需要所做的决策。
- 结果:如果上下文不合适,设计模式可能对代码的其他部分产生负面的影响,开发人员应该了解设计模式的 结果和用途。
模式的分类
- 创建型模式(单例模式是创建型模式的一种)
- 它们的运行机制基于对象的创建方式
- 它们将对象创建的细节隔离开来
- 代码与所创建的对象的类型无关
- 结构型模式(适配器模式是结构型模式的一种)
- 他们致力于设计出能够通过组合获得更强大功能的对象和类的结构
- 重点是简化结构并识别类和对象之间的关系
- 它们主要关注类的继承和组合
- 行为型模式(观察者模式是行为型模式的一种)
- 它们关注对象之间的交互以及对象的响应性
- 对象应该能够交互,同时仍然保持松散耦合
单例设计模式
- 确保类有且只有一个对象被创建
- 为对象提供一个访问点,以使程序可以全局访问该对象
- 控制共享资源的并行访问
代码实现
- 单例实现(经典)
class Singleton:
def __new__(cls):
if not hasattr(cls,"instance"):
cls.instance = super(Singleton,cls).__new__(cls)
return cls.instance
- 单例实现(懒汉式)
class Singleton:
__instance = None
def __init__(self):
if not Singleton.__instance:
pass
else:
pass
@classmethod
def getInstance(self):
if not cls.__instance:
cls.__instance=Singleton()
return cls.__instance
单例和元类
类的定义由它的元类决定,所以创建类A=type(name,base,dict)
- name:类的名称
- base:基类
- dict:属性变量
因此,用元类创建单例模式为:
class MetaSingleton(type):
_instances = {}
def __call__(cls,*args,**kwargs):
if cls not in cls.__instance:
cls._instances[cls] = super(MetaSingleton,cls).__call__(*args,**kwargs)
return cls._instances[cls]
class Database(metaclass=MetaSingleton):
connection = None
def connection(self):
if self.connection is None:
self.connection = sqlite3.connect("db.sqlite3")
self.cursorobj = self.connection.cursor()
return self.cursorobj
单例模式的缺点
- 单例模式具有全局访问权限,全局变量可能在某处被误改,但是开发人员仍然认为它们没有发生变化,而该变量还在应用程序的其他位置被使用
- 可能会对同一对象创建多个引用,由于单例只创建一个对象,因此这个情况下会对同一个对象创建多个引用
- 所有依赖于全局变量的类都会由于一个类的改变而紧耦合为全局数据,从而可能在无意中影响另一个类
工厂模式
工厂模式的优点
- 松耦合,对对象的创建可以独立于类的实现
- 客户端无需了解创建对象的类,但是照样可以使用它来创建对象,他只需要知道需要传递的接口、方法、参数,就能够创建所需类型的对象了,这简化了客户端的实现。
- 可以轻松在工厂中添加其他类来创建其他类型的对象,而这无需更改客户端代码。最简单的情况下,客户端只需要传递另一个参数即可。
- 工厂还可重用现有对象,但是,如果客户端直接创建对象的话,总是创建一个新的对象
工厂模式分类
- 简单工厂模式
允许接口创建对象,但不会暴露对象的创建逻辑
-
工厂方法模式
允许接口创建对象,但是由那个类创建对象,则交由子类决定
- 它具有更大的灵活性,使得代码更加通用,因为它不是单纯的实例化某个类,这样,实现哪个类取决于接口(Product),而不是ConcreteProduct类
- 它们是松耦合的,因为创建对象的代码与使用它的代码是分开的,客户端完全不需要关心要传递哪些参数以及需要实例化哪些类, 由于添加新类更加容易,所以降低了维护成本。
-
抽象工厂模式
抽象工厂是一个能够创建一系列相关的抽象而无需指定/公开其具体类的接口。该模式能够提供其他工厂的对象,在其内部创建其他对象
门面模式
- 它为子系统中的一组接口提供一个统一的接口,并定义了一个高级接口来帮助客户端通过更加简单的方式使用子系统
- 门面所解决的问题是,如何用的单个接口对象来表示复杂的子系统,实际上,它并不是封装子系统,而是对底层子系统进行组合
- 它促进了实现与多个客户端的解耦
最少知识原则
- 在设计系统时,对于创建的每个对象,都应该考察与之交互的类的数量,以及交互的方式
- 遵循这个原则,就能够避免创建许多紧密耦合的类的情况
- 如果类之间存在大量的依赖关系,那么系统就会变的难以维护,如果对系统中的任何一部分进行修改,都可能导致系统的其他部分被无意改变,这意味者系统会退化,是应该坚决避免的
代理模式
- 它能够以更简单的方式表示一个复杂的系统,例如,涉及多个复杂计算或过程的系统应该提供一个更简单的接口,让他充当客户端的代理。
- 它提高了现有的实际对象的安全性,在许多情况下,都不允许客户端直接访问实际对象,这是因为实际对象可能受到恶意活动的危害,这时候,代理就能起到抵御恶意活动的盾牌作用,从而保护实际对象
- 它为不同服务器上的远程对象提供本地接口,一个明显的例子,是客户端希望在远程系统上运行某些命令的分布式系统,但客户端可能没有直接的权限实现这一点,因此它将请求转交给本地对象(代理),然后由远程机器上的代理执行该请求
- 它为消耗大量内存的对象提供了一个轻量级的句柄,有时,你可能不想加载主要对象,除非它们真的非常必要,这是因为实际对象真的很笨重,可能需要消耗大量资源。一个典型的例子是网站用户的个人简介头像,你最好在列表视图中显示简介头像的缩略图,当然,为了展示用户简介的详细介绍,你就需要加载实际图片了。
-
虚拟代理
当客户端请求或访问对象时,才会创建实际对象
-
远程代理
它给位于远程服务器或不同地址空间上的实际对象提供了一个本地表示
-
保护代理
能够控制RealSubject的敏感对象的访问
-
智能代理
在访问对象时进行其他操作
代理模式的优点
- 代理可以通过缓存笨重的对象或频繁访问的对象来提高应用程序的性能
- 代理还提供对于真实主题的访问授权,因此,只有提供合适权限的情况下,这个模式才会接受委派
- 远程代理还便于与可用作网络连接和数据库连接的远程服务器进行交互,并可用于监视系统
观察者模式
行为型模式
它主要关注的是对象的责任,它们用来处理对象之间的交互,以实现更大的功能
行为型模式建议:
- 对象之间应该彼此之间交互,同时还应该是松散耦合的。
在观察者模式中,对象(主题)维护了一个依赖(观察者)列表,以便主题可以使用观察者定义的任何方法通知所有观察者它所发生的变化
观察者模式的主要目标如下:
- 它定义了对象之间的一对多的依赖关系,从而使得一个对象中的任何更改都将自动通知给其他依赖对象
- 他封装了主题的核心组件
观察者模式可用于以下多种场景
- 在分布式系统中实现事件服务
- 用作新闻机构的框架
- 股票市场也是观察者模式的一个大型场景
命令模式
- 方法名称
- 拥有方法的对象
- 方法参数的值
命令行通常使用一下术语:Command、Receiver、Invoker和Client
- Command对象了解Receiver对象的情况,并能调用Receiver对象的方法
- 调用者方法的参数值存储在Command对象中
- 调用者知道如何执行命令
- 客户端用来创建Command对象并设置其接受者
命令模式的主要意图如下:
- 将请求封装成对象
- 可用不同的请求对客户进行参数化
- 允许将请求保存在队列中
- 提供面向对象的回调
命令模式可用于以下各种情景
- 根据需要执行的操作对对象进行参数化
- 将操作添加到队列并在不同地点执行请求
- 创建一个结构来根据较小操作完成高级操作
命令模式的优缺点
命令模式具有以下优点
- 将调用操作的类与知道如何执行该操作的对象解耦
- 提供队列系统后,可以创建一系列命令
- 添加新命令更加容易,并且无需更改现有代码
- 还可以使用命令模式来定义回滚系统,例如,在向导示例中,我们可以编写一个回滚方法
命令行模式具有以下缺点
- 为了实现目标,需要大量的类和对象进行协作,应用程序开发人员为了正确开发这些类,需要倍加小心
- 每个单独的命令都是一个ConcreteCommand类,从而增加了需要实现和维护的类的数量
模板方法模式
- 当多个算法或类实现类似或相同逻辑的时候
- 在子类中实现算法有助于减少重复代码的时候
- 可以让子类利用覆盖实现行为来定义多个算法的时候
模型—视图—控制器
MVC不仅是一种实现用户界面的软件模式,同时也是一种易于修改和维护的架构。
MVC模式的工作机制为:
模型提供数据和业务逻辑(如何查询和存储信息),视图负责数据的展示(如何呈现),而控制器是两者之间的粘合剂,根据用户要求的呈现方式来协调模型和视图。有趣的是,视图和控制器依赖于模型,而不是反过来,这主要是因为用户所关心的是数据。模型可以独立工作,这是MVC模式的关键所在。