目录
引言
设计模式:
(1)定义:对软件设计中普遍存在(反复出现)的各种问题,所提出的解决方案。每一个设计模式系统地命名、解释和评价了面向对象系统中一个重要的和反复出现的设计。
“四人帮”(Gang of Four, GoF)
(2)面向对象的三大特性(顺序不能改,必须是封装、继承、多态):
1.封装:把数据属性和函数封装到一个类中,这种封装体现的是类内和类外;私有和公有属性or函数(如python中私有属性/函数以两个下划线开始,是类外访问不到)
2.继承:那么如果两个类之间想复用代码怎么办? 因为有了封装,则需要继承,来进行类之间的复用代码;
3.多态:python中本身就是多态的语言,在python中无需care。
(3)接口:若干抽象方法的集合。
作用:限制实现接口的类必须按照接口给定的调用方式实现这些方法;对高层模块隐藏了这些类的内部实现。
(4)面向对象设计SOLID原则:
SRP | The Single Responsibility Principle | 单一责任原则 |
OCP | The Open Closed Principle | 开放封闭原则 |
LSP | The Liskov Substitution Principle | 里氏替换原则 |
DIP | The Dependency Inversion Principle | 依赖倒置原则 |
ISP | The Interface Segregation Principle | 接口分离原则 |
1.单一职责原则:不要存在对于一个导致类变更的原因。通俗的说,即一个类只负责一项职责;
2.开放封闭原则:一个设计实体如类、模块和函数应该对扩展开发,对修改关闭。即软件实体应该尽量在不修改原有代码的情况下进行扩展;
3.里氏替换原则:所引用的父类的地方必须能透明地使用其子类对象;
4.依赖倒置原则:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。换言之,应该弄面向接口编程,而不是针对实现编程;
5.接口分离原则:使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。
设计模式分类:
1.创建型模式(5(6)种):【对类的实例化过程进行了抽象,能够将软件模块中对象的创建和对象的使用分离,创建什么(What)、由谁创建(Who)、何时创建(When)】简单工厂模式、工厂方法模式(简单)、抽象工厂模型、创建者模式(复杂、灵活)、原型模式、单例模式
2.结构性模式(7种):【描述如何将类或对象结合在一起形成更大的结构】适配器模式、桥模式、组合模式、装饰模式、外观模式、享元模式、代理模式
3.行为模式(11种):【对在不同的对象之间划分责任和算法的抽象化,可以更加清晰地划分类与对象的职责,并研究系统在运行时实例对象之间的交互】解释器模式、责任链模式、命令模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、访问者模式、模板方法模式
(1)针对创建型模式小结:
抽象工厂模式和建造者模式相比于简单工厂模式和工厂方法模式而言更灵活也更复杂;通常情况下,设计以简单工厂模式或工厂方法模式开始,当你发现设计需要更大的灵活性时,则像更复杂的设计模式演化。
一、试画出简单工厂模式的模式结构图,并对模式进行分析
1.1 简单工厂模式结构图:
图1 简单工厂模式的模式结构图
由图1可知,简单工厂模式包含以下3个角色:
- Factory(工厂角色):工厂角色即工厂类,它是简单工厂模式的核心,负责实现创建所有产品实例的内部逻辑;工厂类可以被外界直接调用,创建所需的产品对象;在工厂类中提供了静态的工厂方法factoryMethod(),它的返回类型为抽象产品类型Product;
- Product(抽象产品角色):它是工厂类创建的所有对象的父类,封装了各种产品对象的公有方法,它的引入将提高系统的灵活性,使得工厂类中只需要定义一个通用的工厂方法,因为所有创建的具体产品对象都是其子类对象;
- ConcreteProduct(具体产品角色):它是简单工厂模式的创建目标,所有被创建的对象都充当这个角色的某个具体类的实例。每一个具体产品角色都继承了抽象产品角色,需要实现在抽象产品中声明的抽象方法。
1.2 简单工厂模式的实现(Java)
在使用简单工厂模式时首先需要对产品类进行重构,不能设计一个包罗万象的产品类,而需根据实际情况设计一个产品层次结构,将所有产品类公共的代码移至抽象产品类,并在抽象产品类中声明一些抽象方法,以供不同的具体产品类来实现。即,抽象产品类代码如下:
public abstract class Product{
//所有产品类的公共业务方法
public void methodSame(){
//公共方法的实现
}
//声明抽象业务方法
public abstract void methodDiff();
}
在具体产品类中实现了抽象产品类中声明的抽象业务方法,不同的具体产品类可以提供不同的实现。对应具体产品类的代码如下:
public class ConcreteProduct extends Product{
//实现业务方法
public void methodDiff(){
//业务方法的实现
}
}
简单工厂模式的核心是工厂类,在没有工厂类之前客户端一般会使用new关键字类直接创建产品对象,而在引入工厂类之后客户端可以通过工厂类来创建产品,在简单工厂模式中工厂类提供了一个静态工厂方法供客户端使用,根据所传入的参数不同可以创建不同的产品对象。其工厂类的代码如下:
public class Factory{
//静态工厂方法
public static Product getProduct(String arg){
Product product = null;
if(arg.equalsIgnoreCase("A")){
product = new ConcreteProductA();
//初始设置product
}else if(arg.equalsIgnoreCase("B")){
product = new ConcreteProductB();
//初始化设置product
}
return product;
}
}
在客户端代码中,通过调用工厂类的工厂方法可得到产品对象。其代码如下:
public class Client{
public static void main(String args[]){
Product product;
product = Factory.getProduct("A");//通过工厂类创建产品对象
product.methodSame();
product.methodDiff();
}
}
1.3 简单工厂模式的优缺点
简单工厂模式提供了专门的工厂类用于创建对象,将对象的创建和对象的使用分离开,它作为一种最简单的工厂模式在软件开发中得到了较为广泛的应用。
优点:
- 工厂类包含必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的职责,而仅仅"消费"产品,简单工厂模式实现了对象创建和使用的分离;
- 客户端无须指定所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,对应复杂的类名,通过简单工厂模式可以在一定程度上减少使用者的记忆量;
- 通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。
缺点:
- 由于工厂类集中了所有产品的创建逻辑,责任过重,一旦不能正常工作,整个系统都要受到影响;
- 使用简单工厂模式识别会增加系统中类的个数(引入了新的工厂类),增加了系统的复杂度和理解难度;
- 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型讲多时有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护;
- 简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成继续继承的等级结构。
1.4 简单工厂模式实现(Python)
#!/usr/bin/env python
# _*_ coding: utf-8 _*_
# @Time :
# @Author :
# @Version:V 0.1
# @File : 简单工厂模式.py
# @desc :
from abc import ABCMeta, abstractmethod
class Payment(metaclass=ABCMeta):
# abstract class
@abstractmethod
def pay(self, money):
pass
class Alipay(Payment):
def __init__(self, use_huabei=False):
self.use_huabei = use_huabei
def pay(self, money):
if self.use_huabei == True:
print("花呗支付%d元" % money)
else:
print("支付宝余额支付%d元" % money)
class WechatPay(Payment):
def pay(self, money):
print("微信支付%d元" % money)
class PaymentFactory:
def create_payment(self, method):
if method == 'alipay':
return Alipay()
elif method == 'wechat':
return WechatPay()
elif method == 'huabei':
return Alipay(use_huabei=True)
else:
raise TypeError("No such payment named %s" % method)
pf = PaymentFactory()
p = pf.create_payment('alipay')
p.pay(100)
二、试画出工厂方法模式的模式结构图,并对模式进行分析
2.1 工厂方法模式结构图:
图2 工厂方法模式的模式结构图
由图2可知,工厂方法模式包含以下4个角色:
- Product(抽象产品):它是定义产品的接口,是工厂方法模式所创建对象的超类型,也就是产品对象的公共父类;
- ConcreteProduct(具体产品):它实现了抽象产品接口,某种类型的具体产品由专门的具体工厂创建,具体工厂和具体产品之间一一对应;
- Factory(抽象工厂):在抽象工厂类中声明了工厂方法(Factory Method),用于返回一个产品。抽象工厂是工厂方法模式的核心,所有创建对象的工厂类都必须实现该接口;
- ConcreteFactory(具体工厂):它是抽象工厂类的子类,实现了在抽象工厂中声明的工厂方法,并可由客户端调用,返回一个具体产品类的实例。
2.2 工厂方法模式的实现(Java)
与简单工厂模式相比,工厂方法模式最重要的特点是引入了抽象工厂角色,抽象工厂可以是接口,也可以是抽象类或者是具体类,即其代码如下:
public interface Factory{
public Product factoryMethod();
}
在抽象工厂中声明了工厂方法但并未实现工厂方法,具体产品对象的创建由其子类负责,客户端针对抽象工厂编程,可在运行时再指定具体工厂类,具体工厂类实现了工厂方法,不同的具体工厂可以创建不同的具体产品。其代码如下:
public class ConcreteFactory implements Factory{
public Product factoryMethod(){
return new ConcreteProduct();
}
}
在实际使用时,具体工厂类在实现工厂方法时除了创建具体产品对象外,还可以负责产品对象的初始化工作以及一些资源和环境配置工作,例如连接数据库、创建文件等。
在客户端代码中,开发只需要关心工厂类即可,不同的具体工厂可以创建不同的产品。其客户端代码片段如下:
Factory factory;
factory = new ConcreteFactory();//可通过配置文件与反射机制实现
Product product;
product = factory.factoryMethod();
2.3 工厂方法模式的优缺点
工厂方法模式是简单工厂模式的延伸,它继承了简单工厂模式的优点,同时还弥补了简单工厂模式的不足。工厂方法模式是使用频率最高的设计模式之一,是很多开源框架和API类库的核心模式
优点:
- 在工厂方法模式中,工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪些具体产品类将被实例化这一细节,用户只关心所需产品对应的工厂,无须关心创建细节,甚至无须知道具体产品类的类名;
- 基于工厂角色和产品角色的多态性设计是工厂方法模式的关键。它能够让工厂自主确定创建何种产品对象,而如何创建这个对象的细节完全封装在具体工厂内部。工厂方法模式之所以又被称为多态工厂模式,正是因为所有的具体工厂类都具有同一抽象父类;
- 使用工厂方法模式的另一个优点是在系统中加入新产品时无须修改抽象工厂和抽象产品提供的接口,无须修改其他的具体工厂和具体产品,而只要添加一个具体工厂和具体产品即可,这样系统的可扩展性也就变得非常好,完全符合开闭原则。
缺点:
- 在添加新产品时需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销;
- 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度
2.4 工厂方法模式实现(Python)
#!/usr/bin/env python
# _*_ coding: utf-8 _*_
# @Time :
# @Author :
# @Version:V 0.1
# @File : 工厂方法模式.py
# @desc :
from abc import ABCMeta, abstractmethod
class Payment(metaclass=ABCMeta):
# abstract class
@abstractmethod
def pay(self, money):
pass
class Alipay(Payment):
def __init__(self, use_huabei=False):
self.use_huabei = use_huabei
def pay(self, money):
if self.use_huabei == True:
print("花呗支付%d元" % money)
else:
print("支付宝余额支付%d元" % money)
class WechatPay(Payment):
def pay(self, money):
print("微信支付%d元" % money)
class BankPay(Payment):
def pay(self, money):
pass
'''
工厂类的接口
'''
class PaymentFactory(metaclass=ABCMeta):
@abstractmethod
def create_payment(self):
pass
'''
具体工厂类
'''
class AlipayFactory(PaymentFactory):
def create_payment(self):
return Alipay()
class WechatPayFactory(PaymentFactory):
def create_payment(self):
return WechatPay()
class HuabeiFactory(PaymentFactory):
def create_payment(self):
return Alipay(use_huabei=True)
class BankFactory(PaymentFactory):
def create_payment(self):
return BankPay(self)
pf = HuabeiFactory()
p = pf.create_payment()
p.pay(100)
三、现需要设计一个程序来读取多种不同类型的图片格式,针对每一种图片格式都设计个图片读取器(ImageReader),如GIF图片读取器(GifReader)用于读取GIF格式的图片、JPG图片读取器(Jpgreader)用于读取JPG格式的图片。图片读取器对象通过图片读取器工厂ImageReaderFactory来创建,ImageReaderFactory是一个抽象类,用于创建图片读取器的工厂方法,其子类GifReaderFactory和 JpgReaderFactory用于创建具体的图片读取器对象。使用工厂方法模式实现该程序的设计。(画出模式结构图,并进行解析)
3.1读取图片格式程序模式结构图
图3 使用工厂方法模式的模式结构图
读取图片程序需要实现了Gif和Jpg格式的图片读取请求,以Gif格式的图片读取请求为例。首先实现ImageReader接口和ImageReaderFactory类,因为继承ImageReaderFactory类的GifReaderFactory类中会创建GifReader,所以接着实现GifReader这个类,最后实现GifReaderFactory类,Test类用于实例化具体对象。
3.2读取图片格式程序实现
ImageReadeFactary类:
Public abstract class ImageReaderFactory {
Public abstract ImageReader createImageReadeer();
}
GifReaderFactory类:
Public class GifReaderFactory extends ImageReaderFactory {
public ImageReader createImageReadeer() {
GifReader gifReader = new GifReader();
return gifReader;
}
}
JpgReaderFactory类:
public class JpgReaderFactory extends ImageReaderFactory {
public ImageReader createImageReadeer() {
JpgReader jpgReader = new JpgReader();
return jpgReader;
}
}
ImageReader类:
public abstract class ImageReader {
public void readImage(){}
}
GifReader类:
public class GifReader extends ImageReader{
public void readImage(){
System.out.println("通过GifReader读取图片");
}
}
JpgReader类:
public class JpgReader extends ImageReader{
public void readImage(){
System.out.println("通过JpgReader读取图片");
}
}
XMLUtil类:
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
public class XMLUtil {
//该方法用于从XML配置文件中提取具体类类名,并返回一个实例对象
public static Object getBean() throws Exception {
//创建DOM文档对象
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document document;
document = documentBuilder.parse(new File("config.xml"));
//获取包含类名的文本结点
NodeList nodeList=document.getElementsByTagName("imageType");
Node imageTypeNode = nodeList.item(0).getFirstChild();
String imageType = imageTypeNode.getNodeValue().trim();
//通过类名生成实例对象并将其返回
Class clazz = Class.forName(imageType+"ReaderFactory");
Object object = clazz.newInstance();
return object;
}
}
Test 类(客户端):
public class Test {
public static void main(String[] args) {
ImageReaderFactory imageReaderFactory = null;
ImageReader imageReader;
try {
imageReaderFactory = (ImageReaderFactory)XMLUtil.getBean();
} catch (Exception e) {
e.printStackTrace();
}
imageReader = imageReaderFactory.createImageReadeer();
imageReader.readImage();
}
}