跳到主要内容

创建型模式

创建型模式

工厂模式

定义了一个创建对象的类,由这个类来封装实例化对象的行为。

public class SimplePizzaFactory {
public Pizza CreatePizza(String ordertype) {
Pizza pizza = null;
if (ordertype.equals("cheese")) {
pizza = new CheesePizza();
} else if (ordertype.equals("greek")) {
pizza = new GreekPizza();
} else if (ordertype.equals("pepper")) {
pizza = new PepperPizza();
}
return pizza;
}
}

存在问题

类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了开闭原则。

开闭原则

面向对象编程领域中,开闭原则规定“软件中的对象(类,模块,函数等等)应该对于扩展是开放的,但是对于修改是封闭的”,这意味着一个实体是允许在不改变它的源代码的前提下变更它的行为。

工厂方法模式

定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象的实例化推迟到子类。

OrderPizza中有个抽象的方法:

abstract Pizza createPizza();

两个工厂类继承OrderPizza并实现抽象方法:

public class LDOrderPizza extends OrderPizza {
Pizza createPizza(String ordertype) {
Pizza pizza = null;
if (ordertype.equals("cheese")) {
pizza = new LDCheesePizza();
} else if (ordertype.equals("pepper")) {
pizza = new LDPepperPizza();
}
return pizza;
}
}
public class NYOrderPizza extends OrderPizza {
Pizza createPizza(String ordertype) {
Pizza pizza = null;
if (ordertype.equals("cheese")) {
pizza = new NYCheesePizza();
} else if (ordertype.equals("pepper")) {
pizza = new NYPepperPizza();
}
return pizza;
}
}

通过不同的工厂会得到不同的实例化的对象,PizzaStroe的代码如下:

public class PizzaStroe {
public static void main(String[] args) {
OrderPizza mOrderPizza;
mOrderPizza = new NYOrderPizza();
}
}

抽象工厂模式

抽象工厂

public interface ProductFactory {
Product manufacture();
}

两个工厂类实现抽象工厂:

/**
* 具体工厂A
*/
public class FactoryA implements ProductFactory{
@Override
public Product manufacture() {
System.out.println("具体工厂A");
return new ProductA();
}
}

package com.llb.factory.factoryMethod;

/**
* 具体工厂B
*/
public class FactoryB implements ProductFactory{
@Override
public Product manufacture() {
System.out.println("具体工厂B");
return new ProductB();
}
}

通过不同的工厂会得到不同的实例化的对象

/**
* 具体产品A
*/
public class ProductA implements Product{
@Override
public void show() {
System.out.println("具体产品A");
}
}

package com.llb.factory.factoryMethod;

/**
* 具体产品B
*/
public class ProductB implements Product{
@Override
public void show() {
System.out.println("具体产品B");
}
}

存在问题:

  1. 一个具体工厂只生产一个具体产品,当工厂和产品增多,类也增多,系统复杂度上升;
  2. 如果需要更换具体工厂里面生产的具体产品还是要更改原有代码,这就违反了开闭原则;

单例模式

预加载

顾名思义,就是预先加载。再进一步解释就是还没有使用该单例对象,但是,该单例对象就已经被加载到内存了。

public class PreloadSingleton {
public static PreloadSingleton instance = new PreloadSingleton();
//其他的类无法实例化单例类的对象
private PreloadSingleton() {
};
public static PreloadSingleton getInstance() {
return instance;
}
}

缺点很明显,没有使用该单例对象,该对象就被加载到了内存,会造成内存的浪费。

懒加载

到用到对应的实例的时候再去加载对象放到内存。

public class Singleton {
private static Singleton instance=null;
private Singleton(){
};
public static Singleton getInstance()
{
if(instance==null)
{
instance=new Singleton();
}
return instance;
}
}

懒加载与线程安全

java初始化一个对象分三步

memory=allocate();//1:初始化内存空间
ctorInstance(memory);//2:初始化对象
instance=memory();//3:设置instance指向刚分配的内存地址
警告

由于JVM为了提高程序执行性能,会对没有依赖关系的代码进行重排序,上面2和3行代码可能被重新排序。我们用两个线程来说明线程是不安全的。线程A和线程B都创建对象。其中,A2和A3的重排序,将导致线程B在B1处判断出instance不为空,线程B接下来将访问instance引用的对象。此时,线程B将会访问到一个还未初始化的对象(线程不安全)。

保证懒加载的线程安全

我们首先想到的就是使用synchronized关键字。synchronized加载getInstace()函数上确实保证了线程的安全。但是,如果要经常的调用getInstance()方法,不管有没有初始化实例,都会唤醒和阻塞线程。为了避免线程的上下文切换消耗大量时间,如果对象已经实例化了,我们没有必要再使用synchronized加锁,直接返回对象。

public class Singleton {
private static Singleton instance = null;
private Singleton() {
};
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}

我们把sychronized加在if(instance==null)判断语句里面,保证instance未实例化的时候才加锁

public class Singleton {
private static Singleton instance = null;
private Singleton() {
};
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}

我们经过上面的讨论知道new一个对象的代码是无法保证顺序性的,因此,我们需要使用另一个关键字volatile保证对象实例化过程的顺序性。

public class Singleton {
private static volatile Singleton instance = null;
private Singleton() {
};
public static synchronized Singleton getInstance() {
if (instance == null) {
synchronized (instance) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}

到此,我们就保证了懒加载的线程安全。

建造者模式/生成器模式(builder模式)

封装一个复杂对象构造过程,并允许按步骤构造。

我们可以将生成器模式理解为,假设我们有一个对象需要建立,这个对象是由多个组件(Component)组合而成,每个组件的建立都比较复杂,但运用组件来建立所需的对象非常简单,所以我们就可以将构建复杂组件的步骤与运用组件构建对象分离,使用builder模式可以建立。或者是对象的变量过多,如果以构造器的方式生成会有很长很长的一串代码,难以识别顺序,容易出错,set方式虽然不易出错,但是代码量过大。

public class Student {

private final String name;
private final int id;
private final int age;
private final int height;

private Student(StudentBuilder studentBuilder) {
this.name = studentBuilder.name;
this.id = studentBuilder.id;
this.age = studentBuilder.age;
this.height = studentBuilder.height;
}

public static class StudentBuilder
{
private String name;
private int id;
private int age;
private int height;

public StudentBuilder(String name,int id)
{
this.name = name;
this.id = id;
}

public StudentBuilder age(int age)
{
this.age=age;
return this;
}

public StudentBuilder height(int height)
{
this.height = height;
return this;
}

public Student build()
{
return new Student(this);
}
}

public String getName() {
return name;
}

public int getId() {
return id;
}

public int getAge() {
return age;
}

public int getHeight() {
return height;
}

@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", id=" + id +
", age=" + age +
", height=" + height +
'}';
}

public static void main(String[] args) {
Student student = new StudentBuilder("codingway",111).age(18).height(2).build();
System.out.println(student.toString());
}
}

原型模式

通过复制现有实例来创建新的实例,无需知道相应类的信息。

简单地理解,其实就是当需要创建一个指定的对象时,我们刚好有一个这样的对象,但是又不能直接使用,我会clone一个一毛一样的新对象来使用;基本上这就是原型模式。关键字:Clone

备注

对象的创建过程往往需要一定时间,直接clone会优化这部分创建所花费的时间。还有一个重要的用途就是保护性拷贝,可以通过返回一个拷贝对象的形式,实现只读的限制。

public class Prototype implements Cloneable {  
public Object clone() throws CloneNotSupportedException {
Prototype proto = (Prototype) super.clone();
return proto;
}
}

例子:

不使用clone,发送十个邮件

public static void main(String[] args) {
int i = 0;
int MAX_COUNT = 10;
EventTemplate et = new EventTemplate("9月份信用卡账单", "国庆抽奖活动...");
long start = System.currentTimeMillis();
while (i < MAX_COUNT) {
// 以下是每封邮件不同的地方
Mail mail = new Mail(et);
mail.setContent(getRandString(5) + ",先生(女士):你的信用卡账单..." + mail.getTail());
mail.setReceiver(getRandString(5) + "@" + getRandString(8) + ".com");
// 然后发送邮件
sendMail(mail);
i++;
}
long end = System.currentTimeMillis();
System.out.println("用时:" + (end - start));
}

// 用时:10001

使用clone,发送十个邮件

public static void main(String[] args) {
int i = 0;
int MAX_COUNT = 10;
EventTemplate et = new EventTemplate("9月份信用卡账单", "国庆抽奖活动...");
long start=System.currentTimeMillis();
Mail mail = new Mail(et);
while (i < MAX_COUNT) {
Mail cloneMail = mail.clone();
mail.setContent(getRandString(5) + ",先生(女士):你的信用卡账单..."
+ mail.getTail());
mail.setReceiver(getRandString(5) + "@" + getRandString(8) + ".com");
sendMail(cloneMail);
i++;
}
long end=System.currentTimeMillis();
System.out.println("用时:"+(end-start));
}

// 用时:1001