目录
总体来说设计模式分为三大类:
创建型模式、结构型模式、行为型模式
创建型模式:用于创建对象
结构型模式:处理类之间的关系
行为型模式:描述类或对象如何交互或怎样分配职责
- 六种:简单工厂模式、工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
- 关注对象的创建过程
- 对类的实例化过程进行抽象,对用户隐藏类 的实例的创建细节
- 创建型模式描述如何将对象的创建和使用分离,让用户在使用对象时无需关心对象的创建细节,从而降低系统 的耦合度,让设计方案更易于修改和扩展
- 关注创建什么(What) 、由谁创建(Who) 、何时创建(When)
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
简单工厂模式
定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类
- 在简单工厂模式中用于创建实例的方法通常是静态(static)方法,因此又被称为静态工厂方法(Static
Factory Method)模式 - 要点:如果需要什么,只需要传入一个正确的参数,就可以获取所需要的对象,而无须知道其创建细节
- 简单工厂模式包含以下3个角色:• Factory(工厂角色)• Product(抽象产品角色)ConcreteProduct(具体产品角色)
- 优点
✓实现了对象创建和使用的分离
✓客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可
✓通过引入配置文件可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性 - 模式缺点
✓ 工厂类集中了所有产品的创建逻辑,职责过重,一旦不能正常工作,整个系统都要受到影响
✓增加系统中类的个数(引入了新的工厂类),增加了系统的复杂度和理解难度
✓ 系统扩展困难,一旦添加新产品不得不修改工厂逻辑
✓ 由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构,工厂类不能得到很好地扩展
实例:
//抽象图表接口,充当抽象产品类
public interface Chart {
public void display();
}
//饼状图类,充当具体产品类
public class PieChart implements Chart {
public PieChart() {
System.out.println("创建饼状图!");
}
public void display() {
System.out.println("显示饼状图!");
}
}
//柱状图类,充当具体产品类
public class HistogramChart implements Chart{
public HistogramChart() {
System.out.println("创建柱状图!");
}
public void display() {
System.out.println("显示柱状图!");
}
}
//图表工厂类,充当工厂类
public class ChartFactory {
//静态工厂方法
public static Chart getChart(String type) {
Chart chart = null;
if (type.equalsIgnoreCase("histogram")) {
chart = new HistogramChart();
System.out.println("初始化设置柱状图!");
}else if (type.equalsIgnoreCase("pie")) {
chart = new PieChart();
System.out.println("初始化设置饼状图!");
}else if (type.equalsIgnoreCase("line")) {
chart = new LineChart();
System.out.println("初始化设置折线图!");
}
return chart;
}
}
//客户端
public class Client {
public static void main(String args[]) {
Chart chart;
//chart = ChartFactory.getChart("histogram"); //通过静态工厂方法创建产品
String type = XMLUtil.getChartType(); //读取配置文件中的参数
chart = ChartFactory.getChart(type); //创建产品对象
chart.display();
}
}
工厂方法模式
虚拟构造器模式(Virtual Constructor Pattern) 或多态工厂模式(Polymorphic Factory Pattern)
- 定义一个用于创建对象的接口,但是让子 类决定将哪一个类实例化。工厂方法模式让一个类的实例化 延迟到其子类。
- 工厂父类负责定义创建产品对象的公共接口,而工厂子类 则负责生成具体的产品对象
- 目的是将产品类的实例化操作延迟到工厂子类中完成,即 通过工厂子类来确定究竟应该实例化哪一个具体产品类
- 工厂方法模式包含以下4个角色: • Product(抽象产品) • ConcreteProduct(具体产品) • Factory(抽象工厂) • ConcreteFactory(具体工厂)
- 模式优点
✓工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节
✓能够让工厂自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部
✓在系统中加入新产品时,完全符合开闭原则 - 模式缺点
✓系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,会给系统带来一些额外的开销
✓增加了系统的抽象性和理解难度
实例:
//日志记录器接口,充当抽象产品角色
public interface Logger {
public void writeLog();
}
//文件日志记录器,充当具体产品角色
public class FileLogger implements Logger {
public void writeLog() {
System.out.println("文件日志记录。");
}
}
//数据库日志记录器,充当具体产品角色
public class DatabaseLogger implements Logger {
public void writeLog() {
System.out.println("数据库日志记录。");
}
}
//日志记录器工厂接口,充当抽象工厂角色
public interface LoggerFactory {
public Logger createLogger(); //抽象工厂方法
}
//文件日志记录器工厂类,充当具体工厂角色
public class FileLoggerFactory implements LoggerFactory {
public Logger createLogger() {
//创建文件日志记录器对象
Logger logger = new FileLogger();
//创建文件,代码省略
return logger;
}
}
//数据库日志记录器工厂类,充当具体工厂角色
public class DatabaseLoggerFactory implements LoggerFactory {
public Logger createLogger() {
//连接数据库,代码省略
//创建数据库日志记录器对象
Logger logger = new DatabaseLogger();
//初始化数据库日志记录器,代码省略
return logger;
}
}
//客户端
public class Client { public static void main(String args[]) {
LoggerFactory factory;
Logger logger;
factory = new FileLoggerFactory();
logger = factory.createLogger();
logger.writeLog();
}
}
抽象工厂模式
- 一个工厂可以生产一系列产品(一族产品),极大减 少了工厂类的数量
- 产品等级结构:产品等级结构即产品的继承结构
- 产品族:产品族是指由同一个工厂生产的,位于不同产品 等级结构中的一组产品
如:
- 适用:当系统所提供的工厂生产的具体产品并不是一个简单的对象,而 是多个位于不同产品等级结构、属于不同类型的具体产品时就可 以使用抽象工厂模式
- 抽象工厂模式:提供一个创建一系列相关或相互依赖对象 的接口,而无须指定它们具体的类
- 包含四个部分: AbstractFactory(抽象工厂)、 ConcreteFactory(具体工厂) 、 AbstractProduct(抽象产品)、ConcreteProduct(具体产品)
e.g:实例:界面皮肤库
//按钮接口:抽象产品
interface Button {
public void display();
}
//Spring按钮类:具体产品
class SpringButton implements Button {
public void display() {
System.out.println("显示浅绿色按钮。");
}
}
//Summer按钮类:具体产品
class SummerButton implements Button {
public void display() {
System.out.println("显示浅蓝色按钮。");
}
}
//文本框接口:抽象产品
interface TextField {
public void display();
}
//Spring文本框类:具体产品
class SpringTextField implements TextField {
public void display() {
System.out.println("显示绿色边框文本框。");
}
}
//Summer文本框类:具体产品
class SummerTextField implements TextField {
public void display() {
System.out.println("显示蓝色边框文本框。");
}
}
//界面皮肤工厂接口:抽象工厂
interface SkinFactory {
public Button createButton();
public TextField createTextField();
}
//Spring皮肤工厂:具体工厂
class SpringSkinFactory implements SkinFactory {
public Button createButton() {
return new SpringButton();
}
public TextField createTextField() {
return new SpringTextField();
}
}
//Summer皮肤工厂:具体工厂
class SummerSkinFactory implements SkinFactory {
public Button createButton() {
return new SummerButton();
}
public TextField createTextField() {
return new SummerTextField();
}
}
//客户端代码
class Client {
public static void main(String args[]) {
//使用抽象层定义
SkinFactory factory;
Button bt;
TextField tf;
factory = (SkinFactory)XMLUtil.getBean(); //配置文件类XMLUtile见下一篇博客
bt = factory.createButton();
tf = factory.createTextField();
bt.display();
tf.display();
}
}
其中配置文件:存储具体工厂类类名
<?xml version="1.0"?>
<config>
<className>SpringSkinFactory</className>
</config>
建造者模式
- 建造者模式:将客户端与包含多个部件的复杂对象的创建过程分离, 客户端无须知道复杂对象的内部组成部分与装配方式, 只需要知道所需建造者的类型即可
- 建造者模式包含以下4个角色: • Builder(抽象建造者) • ConcreteBuilder(具体建造者) • Product(产品) • Director(指挥者)
- 缺点:1、建造者模式所创建的产品一般具有较多 的共同点,其组成部分相似,如果产品 之间的差异性很大,不适合使用建造者 模式,因此其使用范围受到一定的限制 2、如果产品的内部变化复杂,可能会需要 定义很多具体建造者类来实现这种变化, 导致系统变得很庞大,增加了系统的理 解难度和运行成本
e.g:
public class Actor {//充当产品对象
private String type; //角色类型
private String sex; //性别
private String costume; //服装
public void setType(String type) {
this.type = type;
}
public void setSex(String sex) {
this.sex = sex;
}
public void setCostume(String costume) {
this.costume = costume;
}
public String getType() {
return (this.type);
}
public String getSex() {
return (this.sex);
}
public String getCostume() {
return (this.costume);
}
}
//游戏角色建造者,充当抽象建造者
public abstract class ActorBuilder {
protected Actor actor = new Actor();
public abstract void buildType();
public abstract void buildSex();
public abstract void buildCostume();
//返回1个完整的游戏角色对象
public Actor createActor() {
return actor;
}
}
//天使角色建造者,充当具体建造者
public class AngelBuilder extends ActorBuilder {
public void buildType() {
actor.setType("天使");
}
public void buildSex() {
actor.setSex("女");
}
public void buildCostume() {
actor.setCostume("白裙");
}
}
//英雄角色建造者,充当具体建造者
public class HeroBuilder extends ActorBuilder {
public void buildType() {
actor.setType("英雄");
}
public void buildSex() {
actor.setSex("男");
}
public void buildCostume() {
actor.setCostume("盔甲");
}
}
//角色控制器,充当指挥者
public class ActorController {
//逐步构建产品对象
public Actor construct(ActorBuilder ab) {
Actor actor;
ab.buildType();
ab.buildSex();
ab.buildCostume();
actor=ab.createActor();
return actor;
}
}
//建造者模式的应用实例
public class Client {
public static void main(String args[]) {
ActorBuilder ab; //针对抽象建造者编程
ab = new AngelBuilder();//为构建一个天使做准备
//ab = (ActorBuilder)XMLUtil.getBean(); //反射生成具体建造者对象
ActorController ac = new ActorController();
Actor actor;
actor = ac.construct(ab); //通过指挥者创建完整的建造者对象
String type = actor.getType();
System.out.println(type + "的外观:");
System.out.println("性别:" + actor.getSex());
System.out.println("服装:" + actor.getCostume());
}
}
单例模式
- 单例模式有三个要点:一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它 必须自行向整个系统提供这个实例。它的核心结构中只包含一个被称为单例类的特殊类
- 典型例子:任务管理器(多次点击只会出现一次窗口,因为即使出现两个窗口,任务管理器显示的信息应该要是一样的,但这样也就没必要要两个窗口了,浪费资源)
- 优点:
✓提供了对唯一实例的受控访问
✓可以节约系统资源,提高系统的性能
✓允许可变数目的实例(即:多例类模式) - 缺点:
✓扩展困难(缺少抽象层)
✓单例类的职责过重
✓由于自动垃圾回收机制,可能会导致共享的单例对象的状态丢失 - 饿汉式单例类:无须考虑多个线程同时访问的问题;调用速度和反应时间优于懒汉式单例;资源利用效率不及懒汉式单;系统加载时间可能会比较长。
class EagerSingleton{
//静态私有成员变量
private static EagerSingleton instance=new EagerSingleton ();
private EagerSingleton() { }//私有构造函数
//静态公有工厂方法,返回唯一实例
public static EagerSingleton GetInstance(){
return instance;
}
}
- 懒汉式单例类:实现了延迟加载;必须处理好多个线程同时访问的问题;需通过双重检查锁定机制进行控制,将导致系统性能受到一定影响
public class LazySingleton {
private static LazySingleton instance = null;
private LazySingleton() { }
public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
//确保对象的唯一性——单例模式(一):任务管理器
class TaskManager{
//定义 静态 TaskManager类型的 私有 成员变量
private static TaskManager tm = null;
private TaskManager() {……} //初始化窗口
public void displayProcesses() {……} //显示进程
public void displayServices() {……} //显示服务
//由静态方法得到唯一的实例(第一次调用创建一个实例,之后调用返回该实例)
//在类外可以通过类名来访问,无需创建对象
public static TaskManager getInstance(){
if(tm == null){
tm = new TaskManager();
}
return tm;
}
……
}
原型模式
适配器模式
装饰模式
- 装饰模式包含以下4个角色: • Component(抽象构件) • ConcreteComponent(具体构件) • Decorator(抽象装饰类) • ConcreteDecorator(具体装饰类)
- 缺点:
(1) 使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,大量小对象的产生势必会占用更多的系统资源,在一定程序上影响程序的性能。
(2) 装饰模式提供了一种比继承更加灵活机动的解决方案,但同时也意味着比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为繁琐 - 实例分析:
public abstract class Component {
public abstract void display();
}
public class Window extends Component {
public void display() {
System.out.println("显示窗体!");
}
}
public class TextBox extends Component {
public void display() {
System.out.println("显示文本框!");
}
}
public class ListBox extends Component {
public void display() {
System.out.println("显示列表框!");
}
}
public class ComponentDecorator extends Component {
private Component component; //维持对抽象构件类型对象的引用
//注入抽象构件类型的对象
public ComponentDecorator(Component component) {
this.component = component;
}
public void display() {
component.display();
}
}
public class ScrollBarDecorator extends ComponentDecorator {
public ScrollBarDecorator(Component component) {
super(component);
}
public void display() {
this.setScrollBar();
super.display();
}
public void setScrollBar() {
System.out.println("为构件增加滚动条!");
}
}
public class BlackBorderDecorator extends ComponentDecorator {
public BlackBorderDecorator(Component component) {
super(component);
}
public void display() {
this.setBlackBorder();
super.display();
}
public void setBlackBorder() {
System.out.println("为构件增加黑色边框!");
}
}
public class Client {
public static void main(String args[]) { //使用抽象构件定义全部对象
Component component, componentSB, componentBB;
component = new Window(); //创建装饰后的构件对象
componentSB = new ScrollBarDecorator(component); //将装饰了一次的对象注入另一个装饰类中,进行第二次装饰
componentBB = new BlackBorderDecorator(componentSB);
componentBB.display();
}
}
策略模式
- 定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法可以独立于使用它的客户变化
- 策略模式包含以下3个角色:• Context(环境类)• Strategy(抽象策略类)• ConcreteStrategy(具体策略类)
- 模式优点
✓ 提供了对开闭原则的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为
✓ 提供了管理相关的算法族的办法
✓ 提供了一种可以替换继承关系的办法
✓ 可以避免多重条件选择语句
✓ 提供了一种算法的复用机制,不同的环境类可以方便地复用策略类 - 模式缺点
✓客户端必须知道所有的策略类,并自行决定使用哪一个策略类
✓将造成系统产生很多具体策略类
✓无法同时在客户端使用多个策略类
实例:
//电影票类:环境类
public class MovieTicket {
private double price;
private Discount discount; //维持一个对抽象折扣类的引用
public void setPrice(double price) {//原价
this.price = price;
}
//注入一个折扣类对象
public void setDiscount(Discount discount) {
this.discount = discount;
}
public double getPrice() {
//调用折扣类的折扣价计算方法
return discount.calculate(this.price);
}
}
//折扣类:抽象策略类
public interface Discount {
public double calculate(double price);
}
//学生票折扣类:具体策略类
public class StudentDiscount implements Discount {
private final double DISCOUNT = 0.8;
public double calculate(double price) {
System.out.println("学生票:");
return price * DISCOUNT;
}
}
//儿童票折扣类:具体策略类
public class ChildrenDiscount implements Discount {
private final double DISCOUNT = 10;
public double calculate(double price) {
System.out.println("儿童票:");
if(price>=20)
return price - DISCOUNT;
else
return price;
}
}
//VIP会员票折扣类:具体策略类
public class VIPDiscount implements Discount {
private final double DISCOUNT = 0.5;
public double calculate(double price) {
System.out.println("VIP票:");
System.out.println("增加积分!");
return price * DISCOUNT;
}
}
//客户端
public class Client {
public static void main(String args[]) {
MovieTicket mt = new MovieTicket();
double originalPrice = 60.0;
double currentPrice;
mt.setPrice(originalPrice);
System.out.println("原始价为:" + originalPrice);
System.out.println("---------------------------------");
Discount discount;
discount = (Discount)XMLUtil.getBean(); //读取配置文件并反射生成具体折扣对象
mt.setDiscount(discount); //注入折扣对象
currentPrice = mt.getPrice();
System.out.println("折后价为:" + currentPrice);
}
}
外观模式
- 为子系统中的一组接口提供一个统一的入口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用
- 模式优点
✓ 它对客户端屏蔽了子系统组件,减少了客户端所需处理的对象数目,并使得子系统使用起来更加容易
✓ 它实现了子系统与客户端之间的松耦合关系,这使得子系统的变化不会影响到调用它的客户端,只需要调整外观类即可
✓ 一个子系统的修改对其他子系统没有任何影响,而且子系统的内部变化也不会影响到外观对象 - 模式缺点
✓不能很好地限制客户端直接使用子系统类,如果对客户端访问子系统类做太多的限制则减少了可变性和灵活性
✓如果设计不当,增加新的子系统可能需要修改外观类的源代码,违背了开闭原则
//文件读取类:子系统类
public class FileReader {
public String read(String fileNameSrc) {
System.out.print("读取文件,获取明文:");
StringBuffer sb = new StringBuffer();
try{
FileInputStream inFS = new FileInputStream(fileNameSrc);
int data;
while((data = inFS.read())!= -1) {
sb = sb.append((char)data);
}
inFS.close();
System.out.println(sb.toString());
}
catch(FileNotFoundException e) {
System.out.println("文件不存在!");
}
catch(IOException e) {
System.out.println("文件操作错误!");
}
return sb.toString();
}
}
//数据加密类:子系统类
public class CipherMachine {
public String encrypt(String plainText) {
System.out.print("数据加密,将明文转换为密文:");
String es = "";
for(int i = 0; i < plainText.length(); i++) {
String c = String.valueOf(plainText.charAt(i) % 7);
es += c;
}
System.out.println(es);
return es;
}
}
//文件保存类:子系统类
public class FileWriter {
public void write(String encryptStr,String fileNameDes) {
System.out.println("保存密文,写入文件。");
try{
FileOutputStream outFS = new FileOutputStream(fileNameDes);
outFS.write(encryptStr.getBytes());
outFS.close();
}
catch(FileNotFoundException e) {
System.out.println("文件不存在!");
}
catch(IOException e) {
System.out.println("文件操作错误!");
}
}
}
//加密外观类:外观类
public class EncryptFacade {
//维持对其他对象的引用
private FileReader reader;
private CipherMachine cipher;
private FileWriter writer;
public EncryptFacade() {
reader = new FileReader();
cipher = new CipherMachine();
writer = new FileWriter();
}
//调用其他对象的业务方法
public void fileEncrypt(String fileNameSrc, String fileNameDes) {
String plainStr = reader.read(fileNameSrc);
String encryptStr = cipher.encrypt(plainStr);
writer.write(encryptStr,fileNameDes);
}
}
//客户端
public class Client {
public static void main(String args[]) {
EncryptFacade ef = new EncryptFacade(); ef.fileEncrypt("src//designpatterns//facade//src.txt",
"src//designpatterns//facade//des.txt");
}
}
职责链模式
- 避免将一个请求的发送者与接收者耦合在一起,让多个对象都有机会处理请求。将接收请求的对象连接成一条链,并且沿着这条链传递请求,直到有一个对象能够处理它为止
- 职责链模式包含以下两个角色:• Handler(抽象处理者)• ConcreteHandler(具体处理者)
- 模式优点:
✓一个对象无须知道是其他哪一个对象处理其请求,降低了系统的耦合度
✓可简化对象之间的相互连接
✓给对象职责的分配带来更多的灵活性
✓增加一个新的具体请求处理者时无须修改原有系统的代码,只需要在客户端重新建链即可 - 模式缺点
✓不能保证请求一定会被处理
✓对于比较长的职责链,系统性能将受到一定影响,在进行代码调试时不太方便
✓如果建链不当,可能会造成循环调用,将导致系统陷入死循环
实例:
//采购单:请求类
public class PurchaseRequest {
private double amount; //采购金额
private int number; //采购单编号
private String purpose; //采购目的
public PurchaseRequest(double amount, int number, String purpose) {
this.amount = amount; this.number = number; this.purpose = purpose;
}
public void setAmount(double amount) {
this.amount = amount;
}
public double getAmount() {
return this.amount;
}
public void setNumber(int number) {
this.number = number;
}
public int getNumber() {
return this.number;
}
public void setPurpose(String purpose) {
this.purpose = purpose;
}
public String getPurpose() {
return this.purpose;
}
}
//审批者类:抽象处理者
public abstract class Approver {
protected Approver successor; //定义后继对象
protected String name; //审批者姓名
public Approver(String name) {
this.name = name;
}
//设置后继者
public void setSuccessor(Approver successor) {
this.successor = successor;
}
//抽象请求处理方法
public abstract void processRequest(PurchaseRequest request);
}
//主任类:具体处理者
public class Director extends Approver {
public Director(String name) {
super(name);
}
//具体请求处理方法
public void processRequest(PurchaseRequest request) {
if (request.getAmount() < 50000) {//处理请求
System.out.println("主任" + this.name + "审批采购单:" + request.getNumber() +
",金额:" + request.getAmount() + "元,采购目的:" + request.getPurpose() );
}
else {
this.successor.processRequest(request); //转发请求
}
}
}
//董事长类:具体处理者
public class President extends Approver {
public President(String name) {
super(name);
}
//具体请求处理方法
public void processRequest(PurchaseRequest request) {
if (request.getAmount() < 500000) {
System.out.println("董事长" + this.name + "审批采购单:" + request.getNumber() +
",金额:" + request.getAmount() + "元,采购目的:" + request.getPurpose() + "。"); //处理请求
}
else {
this.successor.processRequest(request); //转发请求
}
}
}
public class Client {
public static void main(String[] args) {
Approver wjzhang,gyang,jguo,meeting;
wjzhang = new Director("张无忌"); gyang = new VicePresident("杨过");
jguo = new President("郭靖"); meeting = new Congress("董事会");
//创建职责链
wjzhang.setSuccessor(gyang); gyang.setSuccessor(jguo);
jguo.setSuccessor(meeting);
//创建采购单
PurchaseRequest pr1 = new PurchaseRequest(45000, 10001,"购买倚天剑");
wjzhang.processRequest(pr1);
PurchaseRequest pr2 = new PurchaseRequest(60000, 10002,"购买《葵花宝典》");
wjzhang.processRequest(pr2);
PurchaseRequest pr3 = new PurchaseRequest(160000, 10003,"购买《金刚经》");
wjzhang.processRequest(pr3);
PurchaseRequest pr4 = new PurchaseRequest(800000, 10004,"购买桃花岛");
wjzhang.processRequest(pr4);
}
}