一、构造型模式概念介绍:
1、构造函数:
若要创建一个Java类,通常会提供多个构造函数。构造函数是有用的,尽管只有客户类知道该使用哪个构造函数以
及传递什么参数来创建类。
初始化对象的常见方法是调用new操作符,也可以使用反射。反射提供了能将类型与类型成员像对象一样操作的能
力。
一般情况下,你需要为自己开发的类提供构造函数使其能够被初始化。这些构造函数可能相互调用协作,并且类中
的每个构造函数最终都会去调用超类的构造函数。调用构造函数的常规做法是使用new操作符,但也可以使用反射来初始化和使用
对象。
2.构造型模式的意图:
在设计一个新类时,Java构造函数的这些特性可以提供许多选择。然而,只有在类的用户知道该如何初始化类,以及传递
所需参数时,构造函数才是有效的。
构建者模式:在请求创建对象前,逐步收集创建对象需要的信息;
工厂方法模式:决定推迟实例化对象;
抽象工厂模式:创建一族具有某些共同特征的对象;
原型模式:根据现有对象创建一个新的对象;
备忘录模式:通过包含内部状态的静态版本重新构建一个对象。
每种设计模式的意图都是为了解决某种场景下的问题。构造型模式的设计就是为了让客户类不通过类构造函数来创建
对象。
二、构建者(Builder)模式:
当创建一个对象时,并不一定拥有创建该对象的全部信息,如需要逐步获取创建对象的信息,更方便的做法就是分步骤构建
对象。这种情况通常发生在构建解释器和用户界面上。或者,当类的构造函数过于复杂,却对类的重点功能没有太大影响时,就
可能需要让类变得更小一些。
构建者模式的意图是将类的构建逻辑转移到类的实例化外部。
1.构建者的角色及实例示例:
Builder:为创建一个产品对象的各个部件指定抽象接口:
public interface PersonBuilder {
void buildHead();
void buildBody();
void buildFoot();
Person buildPerson();
}
ConcreateBuilder:实现Builder的接口以构造和装配该产品的各个部件,定义并明确它所创建的表示,并提供一个检索
产品的接口。
public class ManBuilder implements PersonBuilder {
Person person;
public ManBuilder() {
person = new Man();
}
public void buildbody() {
person.setBody("建造男人的身体");
}
public void buildFoot() {
person.setFoot("建造男人的脚");
}
public void buildHead() {
person.setHead("建造男人的头");
}
public Person buildPerson() {
return person;
}
}
Director:构造一个使用Bulider接口的对象,指导构建过程;
public class PersonDirector {
public Person constructPerson(PersonBuilder pb) {
pb.buildHead();
pb.buildBody();
pb.buildFoot();
return pb.buildPerson();
}
}
Product:表示被构造的复杂对象。ConcreateBuilder创建产品的内部表示并定义它的装配过程,包含定义组成部件的类,
包括将这些部件装配成最终产品的接口。
public class Person {
private String head;
private String body;
private String foot;
public String getHead() {
return head;
}
public void setHead(String head) {
this.head = head;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public String getFoot() {
return foot;
}
public void setFoot(String foot) {
this.foot = foot;
}
}
public class Man extends Person {
public Man(){
System.out.println(“开始建造男人");
}
}
public class Woman extends Person {
public Woman(){
System.out.println(“开始建造女人");
}
}
2.建造者模式的变种:
建造者模式在使用过程可以演化出多种形式:
如果具体的被创建对象只有一个的话,可以省略抽象的Builder和Director,让ConcreateBuilder自己扮演指导者和建造者
双重角色,甚至ConcreateBuilder也可以放到Product里面实现。
在《Effective Java》中提到“遇到多个构造器参数时要考虑用构建器”,其实这里的构建器就属于建造者模式,只是里面
把四个角色都放在具体的产品里了。
public class Man {
private String head;
private String body;
private String foot;
public String getHead() {
return head;
}
public void setHead(String head) {
this.head = head;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public String getFoot() {
return foot;
}
public void setFoot(String foot) {
this.foot = foot;
}
}
public class ManBuilder{
Man man;
public ManBuilder() {
man = new Man();
}
public void buildbody() {
man.setBody("建造男人的身体");
}
public void buildFoot() {
man.setFoot("建造男人的脚");
}
public void buildHead() {
man.setHead("建造男人的头");
}
public Man builderMan() {
buildHead();
buildBody();
buildFoot();
return man;
}
}
3.小结:
构建者模式将复杂对象的构建逻辑从对象本身抽离出来,这样能够简化复杂的对象。构建者关注目标类的构建过程,目标类
关注合法实例的业务本身。这种做的好处是在实例化目标类前,确保得到的是一个有效的对象,并且不会让构建逻辑出现在目标
本身。构建者构建类对象的过程通常是分步骤的,这使得该模式通常 被应用于解析文本以创建对象的场景
二、工厂方法(Factory Method)模式:
创建类时,通常可以同时定义多个构造函数,然后让它们创建类的实例。然而有时候,客户代码虽然需要某个对象,却不关
心或者不需要关心这个对象究竟是由哪个类创建而来的。
工厂方法模式的意图是定义一个用于创建对象的接口,并控制返回哪个类的实例。
1.识别工厂方法:
工厂方法模式不仅要求有一个创建新对象的方法,还要让客户代码无须了解具体实例化的类。工厂方法模式通常包含了
若干类类,这些类实现了相同的操作。返回了相同的抽象类型,然而这些操作的内部,实际上却实例化了不同的类,并且,这些类
都实现了上述抽象类型。当客户代码请求一个新对象时,这个新对象由哪个类实例化,取决于工厂对象接收创建请求时的行为。
2.控制要实例化的类:
客户代码使用类的某个构造函数来实例化类。但是有时候,客户代码并不知道使用哪个类去创建它所需要的对象。如在迭
代器模式中,客户代码需要的迭代器的类取决于客户代码想要遍历的容器的类型。
3. 并行层次结构中的工厂方法模式:
在使用并行层次结构对问题域进行建模时,常常会使用工厂方法模式。一个并行层次结构是一对类层次结构。其中
一个类在一个层次,与其相关的类的另一层次。当将现有类层次结构的部分行为移除该层次后。并行层次结构就会显露出来。
4.工厂方法模式的角色及实例示例:
工厂方法模式核心精神是封装类中不变的部分,提取其中个性化善变的部分为独立类,通过依赖注入以达到解耦、复用
和方便后期维护拓展的目的。它的核心结构有抽象产品类、具体产品类、抽象产品、具体产品:
抽象产品角色:包含具体产品继承的父类或者实现的接口,一般是抽象类或接口
public abstract class Product {
/**
* 产品类的公共方法
*/
public void method1(){
//业务逻辑处理
}
//抽象方法
public abstract void method2();
}
具体产品类:具体工厂角色所创建的对象就是此角色的实例
public class ConcreteProduct1 extends Product{
/**
* 具体产品类
*/
@Override
public void method2() {
// 业务逻辑处理
}
}
public class ConcreteProduct2 extends Product{
/**
* 具体产品类
*/
@Override
public void method2() {
// 业务逻辑处理
}
}
抽象工厂类:是模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或必须继承的父类。
public class ConcreteCreator extends Creator{
/**
* 具体工厂类
*/
@SuppressWarnings("unchecked")
@Override
public <T extends Product> T createProduct(Class<T> c) {
Product product = null;
try {
product = (Product) Class.forName(c.getName()).newInstance();
} catch (Exception e) {
// 异常处理
}
return (T)product;
}
}
场景类:
public class Client {
/**
* 场景类
*/
@SuppressWarnings("unused")
public static void main(String[] args) {
Creator creator = new ConcreteCreator();
Product product = creator.createProduct(ConcreteProduct1.class);
/**
* 继续业务处理
*/
}
}
5.小结;
当你不想让客户代码决定实例化哪个类时,常常可以运用工厂方法模式。共产方法模式还可以用于当客户代码不知道它
需要创建哪个对象类的时候。另外,在并行类层次结构中使用该模式可以避免类的规模过于庞大。工厂方法模式可以根据一个类
层次中的子类,确定另一个相关层次中哪个一个类被实例化,从而建立对应的并行层次结构。
三、抽象工厂(Abstract Factory):
在创建对象时,有时会指定具体类去实例化一个对象。可以使用工厂方法模式来定义一个外部方法以决定实例化哪个类。但
有时,控制实例化哪个类的因素可能与很多类息息相关。
抽象工厂模式又名工具箱,其意图时允许创建一族相关或互相依赖的对象。
1.抽象工厂模式代码:
产品类:
//发动机以及型号
public interface Engine {
}
public class EngineA extends Engine{
public EngineA(){
System.out.println("制造-->EngineA");
}
}
public class EngineBextends Engine{
public EngineB(){
System.out.println("制造-->EngineB");
}
}
//空调以及型号
public interface Aircondition {
}
public class AirconditionA extends Aircondition{
public AirconditionA(){
System.out.println("制造-->AirconditionA");
}
}
public class AirconditionB extends Aircondition{
public AirconditionB(){
System.out.println("制造-->AirconditionB");
}
}
创建工厂类:
//创建工厂的接口
public interface AbstractFactory {
//制造发动机
public Engine createEngine();
//制造空调
public Aircondition createAircondition();
}
//为宝马320系列生产配件
public class FactoryBMW320 implements AbstractFactory{
@Override
public Engine createEngine() {
return new EngineA();
}
@Override
public Aircondition createAircondition() {
return new AirconditionA();
}
}
//宝马523系列
public class FactoryBMW523 implements AbstractFactory {
@Override
public Engine createEngine() {
return new EngineB();
}
@Override
public Aircondition createAircondition() {
return new AirconditionB();
}
}
客户:
public class Customer {
public static void main(String[] args){
//生产宝马320系列配件
FactoryBMW320 factoryBMW320 = new FactoryBMW320();
factoryBMW320.createEngine();
factoryBMW320.createAircondition();
//生产宝马523系列配件
FactoryBMW523 factoryBMW523 = new FactoryBMW523();
factoryBMW320.createEngine();
factoryBMW320.createAircondition();
}
}
四、原型(Prototype)模式:
设计一个类时,通常会提供构造函数,使得客户端应用程序能够通过它去创建对象。但在某些情况下,可能不允许类的调
用者直接调用构造函数。在构造型设计模式中,如构建者模式、工厂方法模式、抽象工厂模式都可以避免客户端直接电影构造
函数。这些模式都是通过引入新类,代表客户端代码实例化合适的类对象。原型模式创建对象的方式与这些模式殊途同归。
原型模式的意图是通过复制一个现有的对象来生成新的对象,而不是通过实例化的方式。
1.作为工厂的原型:
创建对象的常规方法是调用类的构造函数。原型模式提供了灵活的解决方案,你可以在运行时决定使用哪个对象。然而
Java程序开发红的原型方法并不允许对象拥有与父对象不同的方法。因此可能需要重现考虑原型模式的利弊,甚至需要先试试原
型模式是否符合需求。为了使用原型模式,需要掌握Java语言的复杂对象机制。
2.利用克隆进行原型化:
原型模式的意图是通过复制一个样本来创建新对象。当复制一个对象时,新对象将拥有与原对象相同的属性和方法。
当然,这个新对象还可以继承部分甚至全部原对象的数据值。
3.原型模式的角色扮演及实例示例:
实现Cloneable接口。在Java中有一个Cloneable接口,他的作用只有一个,就是在运行时通知虚拟机可以安全地在实
现了此接口的类上使用clone方法。在Java虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出
CloneNotSupportedException异常。
重写Object类中的clone方法。Java中,所有类的父类都是Object类,Object类中有一个clone方法,作用是返回对象
的一个拷贝,但是其作用域protected类型,一般的类无法调用,因此,在原型类需要将clone方法的作用域修改为public类型。
代码实现:
class Prototype implements Cloneable {
public Prototype clone(){
Prototype prototype = null;
try{
prototype = (Prototype)super.clone();
}catch(CloneNotSupportedException e){
e.printStackTrace();
}
return prototype;
}
}
class ConcretePrototype extends Prototype{
public void show(){
System.out.println("原型模式实现类");
}
}
public class Client {
public static void main(String[] args){
ConcretePrototype cp = new ConcretePrototype();
for(int i=0; i< 10; i++){
ConcretePrototype clonecp = (ConcretePrototype)cp.clone();
clonecp.show();
}
}
}
使用原型模式创建对象比直接new一个对象在性能上要好得多,因为Object类的clone方法是一个本地方法,它直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显。
五、备忘录(Memento)模式:
有时候想创建的对象已经在系统中存在。如当用户执行撤销操作,是系统回滚到之前的某一状态,或重新执行之前搁置的
工作时,就会出现这种情形。
备忘录模式的意图是为对象状态提供储存和恢复功能。
1.备忘录模式的结构:
发起者角色(Originator):负责创建一个备忘录用以记录当前时刻它的内部状态,可以使用备忘录恢复内部状态。
public class Emp {
private String name;
private int age;
private int salary;
//进行备份操作,并返回一个备忘录对象
public EmpMemento memento(){
return new EmpMemento(this);
}
//进行数据恢复,恢复成备忘录中对象的值
public void recovery(EmpMemento em){
this.name=em.getName();
this.age=em.getAge();
this.salary=em.getSalary();
}
public Emp(String name, int age, int salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
}
备忘录角色(Memento):负责存储Originator对象的内部状态,并可以防止Originator以外的其他对象访问备忘录。
public class EmpMemento {
private String name;
private int age;
private int salary;
public EmpMemento(Emp emp){
this.name=emp.getName();
this.age=emp.getAge();
this.salary=emp.getSalary();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
}
管理者觉得(CareTake):负责保存好备忘录。
/**
* 管理者角色:负责管理备忘录类,示例只保存了一个备忘录对象,可以通过设置栈保存多个对象
*/
public class CareTaker {
private EmpMemento memento;
public EmpMemento getMemento() {
return memento;
}
public void setMemento(EmpMemento memento) {
this.memento = memento;
}
}
2.小结:
借助备忘录模式,可以捕捉对象的状态,以便于将来把对象恢复为以前的状态。具体采用哪种方式来储存对象状态,取
决于对象状态需保存时间的长短。为了支持对象跨多个会话的持久性储存,可以使用对象序列化或其他方式来保存备忘录。