Java的23种设计模式详解整理之创建型模式


最近重新阅读"四巨头"的设计模式. 对一些设计模式有了更多的理解. 原著中的例子是C++写的,不好理解. 这里我换成了Java, 代码示例仅供参考,没有具体实现.

介于个人水平有限,如有纰漏,请指正.有问题的朋友可以私信我或者发我邮箱(请到我主页查看),我看到就会回复. 希望和大家一起进步.

工作中有时候最困难的不是怎么去实现一个功能,而是怎么去设计一个功能.我常常会因为频繁改动需求大费脑筋.之后我在思考如何将一个功能在设计之初就做好扩展的准备,防止需求变动导致大面积的修改.code之前想好设计会事半功倍.

废话不说,开始!

Java面向对象少不了复杂对象的创建, 如何创建复杂类型对象,就是我们要研究的课题, 那么就少不了创建类型的模式

一,创建型模式: Abstract Factory 模式, Builder模式, Factory Method模式 以及Prototype模式

Factory Method 模式和Prototype模式这里不做过多解释. 重点关注前两个.

abstract factory 抽象工厂

1, 意图

提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类

注: 1) 这里注意一下, Abstract Factory创建的是一系列相关或相互依赖的对象

2) 何为相关或依赖? 相关或依赖可以理解为构建composite object(复杂对象)所需要的components(组件)

2, 案例

迷宫(Maze):包含有房间(Room), 门(Door), 墙(Wall).

有两种迷宫(Maze): Enchanted(魔法的) 和 Bomed(可爆炸的),

如何生成这些Maze?

3,解决方式

定义一个抽象的MazeFactory, 这个接口声明了用来创建每一类Maze组件的接口. 每一Maze组件都有一个抽象类,而具体子类则实现特定Maze.而对于每一个抽象Maze组件类, MazeFactory接口都有一个返回新Maze组件的对象的操作.客户端调用组件实例,但是客户端并不知道具体实例类型.

4,适用场景

1) 一个系统要独立于它的产品的创建,组合和表示

2) 一个产品需要多个产品系列装配

5, 参与者分析

AbstractFactory(MazeFactory)

--声明一个创建抽象产品对象的操作接口

ConcreteFactory(EnchantedMazeFactory,BomedMazeFactory)

--实现创建具体产品对象的操作

AbstractProduct(Room, Door, Wall)

--为一类产品对象声明一个接口

ConcreteProduct(EnchantedMaze,BomedMaze)

--定义一个将被相应的具体工厂创建的产品对象

--实现AbstractProduct接口

Client

--仅仅使用AbstractFactory和AbstractProduct类声明的接口

6,Abstract Factory模式分析

1) 分离了具体的类

2) 易于产品交换

抽象工厂创建了一个完整的产品系列,可以通过改变具体实例,来交换不同的产品.BomedMazeFactory可以直接替换EnchantedMazeFactory.

3) 有利于产品的一致性

一个应用一次只能使用同一个系列中的对象

4)  难以支持新产品 

如果添加新的产品,就需要扩展AbstractFactory及其子类

7, 实现

1)      Factory应该是单例的

2)      AbstractFactory只负责抽象,具体实现在ConcreteFactory中. 可以为每一个产品定义一个工厂方法.而具体的工厂将为每个产品override该Factory指定的方法(前提是每个产品系列都需要有一个新的工厂类,也就是产品差别小)

8, 代码实现

定义model

public class Door {}
public class Maze {
    public void addRoom(Roomroom1);
}
public class Room {}
public class Wall {}

定义Factory接口

public interface MazeFactory {
   // make a maze
    public Maze makeMaze();
   // make wall
    public Wall makeWall();
   // make room
    public Room makeRoom(intn);
   // make door
    public Door makeDoor(Roomroom1, Room room2);
}

定义实现,

public class EnchantedMazeFactoryimplements MazeFactory {
    @Override
    public Maze makeMaze();
    @Override
    public Wall makeWall();
    @Override
    public Room makeRoom(intn);
    @Override
    public Door makeDoor(Roomroom1, Room room2);
}
 
public class BomedMazeFactory implements MazeFactory {
    @Override
    public Maze makeMaze();
    @Override
    public Wall makeWall();
    @Override
    public Room makeRoom(intn);
    @Override
    public Door makeDoor(Roomroom1, Room room2);
}
 
publicclass MazeGame {
    public Maze createMaze(MazeFactoryfactory){
      // make maze
       Maze maze =factory.makeMaze();
     // make room
       Room room1 =factory.makeRoom(1);
       Room room2 =factory.makeRoom(2);
    // make door
       Door door =factory.makeDoor(room1,room2);
      // add room
       maze.addRoom(room1);
       maze.addRoom(room2);
       return maze;
    }
}

注意:这里的Factory实际生产的不是一个完整的Maze, 而是一个个单独的组件, 然后组件通过MazeGame.createMaze方法才组装起来

定义client

    @Test
    public void test() {
       MazeGame game =new MazeGame();
       MazeFactory factory =new EnchantedMazeFactory();
       //factory = newBomedMazeFactory();
       Mazemaze = game.createMaze(factory);
    }

注意:这里不需要额外的get方法, 而是直接返回maze对象

 builder(生成器)

1, 意图

将一个复杂对象的构建与它的表示分离,使得不同的构建过程可以创建不同的表示

2, 案例

一个RTF(Rich Text Format)文档交换格式的阅读器能将RTF转换为多种正文格式, 该阅读器可以将RTF文档转化为普通的文本或者其他.

3, 可能遇到的问题

可能转化的数目是无限的,因此要能够很容易实现新的转换的增加,同时不改变RTF阅读器.

4,解决

可以将RTF转换成另一种正文表示的TextConvert对象配置这个RTFReader类. 当RTFReader对RTF文档进行分析,它使用TextConvert去转换,无论何时Reader识别了一个RTF标记,它都发送一个请求给TextConvert去转换这个标记. TextConvert对象负责进行数据转换以及用特定格式表示该标记.

每一个转化器类在该模式中称为生成器builder,而阅读器则称为导向器director. Builder模式

将分析文本格式的算法与描述怎样创建和表示一个转换后格式的算法分离出来.这样可以重用RTFReader的语法分析算法, 根据文档创建不同的正文表示则仅仅需要不同的TextConvert就ok.

5,适用场景

1)创建复杂对象的算法应该独立于该对象的组成部分以及他们的装配方式时

2)当构造过程必须允许被构造的对象有不同的表示

6,分析这个过程的参与者

Builder(TextConvert)

--为创建一个Product对象的各个部件指定抽象接口

ConcreteBuilder(ASCIIConvert, TeXConvert,TextWidgetConvert)

--实现Builder的接口,用来构造和装配Product的各个部件

Director(RTFReader)

--构造一个使用Builder的实例

Product(ASCIIText. TeXText, TextWidget)

--表示被构造的复杂对象. ConcreteBuilder创建该产品内部表示并定义它的装配过程

--包含定义组成部件的类,包括将这些部件装配成最终产品的接口

7,builder模式的优点

1)可以改变一个产品的内部表示:

Builder对象提供给Director一个构造产品的抽象接口. 该接口使得生成器隐藏这个产品的表示和内部结构.同时隐藏了产品是如何装配的. 那么对于不同的Produc表示方式,只需要实现这个接口就OK

2)将构造代码和表示代码分开:

Builder封装了创建和表示,外界不需要了解定义产品内部结构的类的所有信息;这些类是不出现在Builder接口中的.每个ConcreteBUilder包含了创建和装配一个特定产品的所有代码.

3)精细控制构建过程:

Builder和直接生成产品的创建模型不同, builder在Direct的控制下一步一步构造产品的.当product完成时, Director才会get到result.因此builder更能反映product的构造过程,可以更精细控制product内部结构

8,实现分析

Builder会为每个构件(Director要求的构件)定义对应的操作.具体的操作实现会在ConcreteBuilder中实现

1)装配和构造接口

生成器逐步构造产品,因此builder类需要高度抽象.

2)product为什么没有抽象类,

考虑到builder生产的product结构差距较大,难以抽象出父类.ASCIIText和TextWidget对象抽象不出公用接口,并且抽象出这样的接口没有任何意义. 因为client给Director适配具体的Builder

3)builder中缺省的方法是空,用于override

案例及分析

Model定义

public class Room {
    private int n;
}
public class Maze {
    private int roomNo;
    public Room roomNo(intn);
    public void add(Roomroom);
}

定义Director

public class MazeGame {
    Maze cteateMaze(MazeBuilderbuilder) {
        builder.buildMaze();
        builder.buildRoom();
        builder.buildDoor();
        return builder.getMaze();
    }
    Maze createComplexMaze(MazeBuilderbuilder) {
        builder.buildRoom();
        builder.buildRoom();
        returnbuilder.getMaze();
    }
}

注意:这里的Builder是没有返回值的,也就是说,对象被封装在了builder中,装配工作将在builder中进行, MazeGame不需要知道装配细节

定义Builder

public interface MazeBuilder {
    public void buildMaze();
    public void buildRoom(introom);
    public void buildDoor(introomFrom,introomTo);
    public Maze getMaze();
    public int getCount();
}

定义Builder的子类,创建标准的Maze

public class StandardMazeBuilder implements MazeBuilder {
    private MazecurrentMaze;
    private DirectioncommonWall;
    public StandardMazeBuilder() {
        this.currentMaze = new Maze();
    } 
    public void buildMaze();
    public void buildRoom(int room);
    public void buildDoor(int roomFrom, int roomTo);
    public Maze getMaze();
}

定义Builder的子类,不生成迷宫, 只记录创建maze数量的builder

public class CountingMazeBuilderimplements MazeBuilder {
    public void buildMaze();
    public void buildRoom(introom) ;
    public void buildDoor(introomFrom,int roomTo) ;
    public Maze getMaze();
    public int getCount();
}

UT

@Test

 public void test() {
        MazeGame game =new MazeGame();
        MazeBuilder builder =new StandardMazeBuilder();
        // game.cteateMaze(builder);
        game.createComplexMaze(builder);
        Maze maze =builder.getMaze();
    }

注意:这里和abstract factory不同,使用了单独的get方法,而不是像abstract factory一样直接返回对象

总结:

1,builder模式的目的是将模型的创建和表示分离开,所以当模型有多个表示的时候可以考虑使用这种模式

2,builder和abstract factory比较

builder和abstract factory很相似,都可以创建复杂的对象.

build是精细控制一步一步创建对象.abstractfactory侧重于多个类型的复杂对象创建.

builder在最后通过get得到复杂对象.而abstractfactory则是立即return

复杂的对象建议使用Builder,可以在ConcreteBuilder中定义具体细节.

 附加:关于Prototype模式

 1, 意图

从源对象克隆一个目标对象( A克隆成A‘)

2, 如何实现

方法一(浅拷贝):

java为克隆提供了Cloneable克隆接口,实现这个接口就ok

方法二(深拷贝):

实现Serializer序列化接口, 并使用对象流clone新对象,这种clone适合深拷贝

参考代码:

source是源对象,target 是克隆的目标对象

     ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
      // set the bytes of the object to an byte array.
      ObjectOutputStream out =new ObjectOutputStream(arrayOutputStream);
    // the source had implemented theSerializable interface
      out.writeObject(source);
      // get the array metioned before
      ByteArrayInputStream arrayInputStream =new ByteArrayInputStream(arrayOutputStream.toByteArray());
      ObjectInputStream input= new ObjectInputStream(arrayInputStream);
      Target target = input.readObject();

注意:这里使用的是ByteArrayOutputStrea和ByteArrayInputStream

时间: 2024-10-13 00:37:44

Java的23种设计模式详解整理之创建型模式的相关文章

JAVA:23种设计模式详解(转)

设计模式(Design Patterns) ——可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代 码可靠性. 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样.项目中合理的运用 设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我

JAVA:23种设计模式详解(转)2

我们接着讨论设计模式,上篇文章我讲完了5种创建型模式,这章开始,我将讲下7种结构型模式:适配器模式.装饰模式.代理模式.外观模式.桥接模式.组合模式.享元模式.其中对象的适配器模式是各种模式的起源,我们看下面的图: 适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题.主要分为三类:类的适配器模式.对象的适配器模式.接口的适配器模式.首先,我们来看看类的适配器模式,先看类图: 核心思想就是:有一个Source类,拥有一个方法,待适配,目标接口时

23种设计模式介绍(一)---- 创建型模式

由于设计模式篇幅比较大,如果在一篇文章讲完所有的设计模式的话不利于阅读.于是我把它分为三篇文章 23种设计模式介绍(一)---- 创建型模式 23种设计模式介绍(二)---- 结构型模式 23种设计模式介绍(三)---- 行为型模式 由于设计模式都是比较抽象的概念,所以大家一定要确保看懂类图,而后再自己写代码加强记忆. 简介 设计模式分为三大类: 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接

23种设计模式介绍(三)---- 行为型模式

由于设计模式篇幅比较大,如果在一篇文章讲完所有的设计模式的话不利于阅读.于是我把它分为三篇文章 23种设计模式介绍(一)---- 创建型模式 23种设计模式介绍(二)---- 结构型模式 23种设计模式介绍(三)---- 行为型模式 设计模式都是比较抽象的概念,所以大家一定要确保看懂类图,而后再自己写代码加强记忆. 概述 行为型模式一共有11种: 模板方法模式(Template Method) 策略模式(Strategy) 命令模式(Command) 中介者模式(Mediator) 观察者模式(

Java开发中的23种设计模式详解(转)

设计模式(Design Patterns) --可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样.项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周

0. Java开发中的23种设计模式详解(转)

设计模式(Design Patterns) ——可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样.项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周

Java开发中的23种设计模式详解

设计模式(Design Patterns) ——可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样.项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周

Java开发中的23种设计模式详解(转载)

设计模式(Design Patterns) --可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样.项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周

Java开发中的23种设计模式详解之一:5种创建型模式

一.设计模式的分类 总体来说设计模式分为三大类: 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接模式.组合模式.享元模式. 行为型模式,共十一种:策略模式.模板方法模式.观察者模式.迭代子模式.责任链模式.命令模式.备忘录模式.状态模式.访问者模式.中介者模式.解释器模式. 其实还有两类:并发型模式和线程池模式.用一个图片来整体描述一下: 二.设计模式的六大原则 1.开闭原则(Open Clo