创建型模式的主要关注点是"怎样创建对象?“, 它的主要特点是"将对象的创建与使用分离”。
这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。
创建型模式分为:
单例模式(Singleton Pattern)是Java中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
单例模式的主要有以下角色:
单例设计模式分类两种:
public class Singleton{
//私有构造方法
private Singleton(){}
private static Singleton instance = new Singleton();
public static Singleton getInstance(){
return instance;
}
}
public class Client{
public static void main(String[] args){
Singleton instance = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance == instance2); //true
}
}
public class Singleton{
private Singleton(){}
private static Singleton instance;
static {
instance = new Singleton();
}
public static Singleton getInstance(){
return instance;
}
}
public class Client{
public static void main(String[] args){
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance1 == instance2);//true
}
}
说明:
该方式在成员位置声明Singleton类型的静态变量,而对象的创建是在静态代码块中,也是对着类的加载而创建。所以和饿汉式的方式是1基本上一样,当然该方式也存在内存浪费问题。
枚举类实现单例模式是极力推荐的单例实现模式,因为枚举类型是线程安全的,并且只会装载一次,设计者充分的利用了枚举的这个特性来实现单例模式,枚举的写法非常简单,而且枚举类型是所有单例实现中唯一一种不会被破坏的单例实现模式。
public enum Singleton{
INSTANCE;
}
说明:
枚举方式属于恶汉方式
public class Singleton{
private Singleton(){}
private static Singleton instance;
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
public class Client{
public static void main(String[] args){
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance1 == instance2);//true
}
}
public class Singleton{
private Singleton(){}
private static Singleton instance;
public synchronized static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
public class Client{
public static void main(String[] args){
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance1 == instance2);//true
}
}
说明:
该方法也实现了懒加载效果,同时解决了线程安全问题。但是在getInstance()方法上添加synchronized关键字,导致该方法的执行效果特别低。从上面代码我们可以看出,其实在初始化instance的时候才会出现线程安全问题,一旦初始化完成就不存在了。
再来讨论一下懒汉模式中加锁问题,对于getInstance()方法来说,绝大部分的操作都是读操作,读操作是线程安全的,所以我们没必让每个线程必须持有锁才能调用该方法,我们需要调整加锁的时机。由此也产生了一种新的实现模式:双重检查锁模式
public class Singleton{
private Singleton(){}
private static Singleton instance;
public static Singleton getInstance(){
//第一次判断,如果instance的值不为null, 不需要抢占锁,直接返回对象
if(instance == null){
synchronized(Singleton.class){
//第二次判断
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
双重检查锁模式是一种非常好的单例实现模式,解决了单例,性能,线程安全问题,上面的双重检测锁模式看上去完美无缺,其实是存在问题,在多线程的情况下,可能会出现空指针问题,出现问题的原因是JVM在实例化对象的时候会进行优化和指令重排序操作。
要解决双重检查锁带来空指针异常问题,只需要使用volatile关键字,volatile关键字可以保证可见性和有序性。
public class Singleton{
private Singleton(){}
private static volatile Singleton instance;
public static Singleton getInstance(){
if(instance == null){
synchronized(Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
说明:
添加volatile关键字之后的双重检查锁模式是一种比较好的单例实现模式,能够保证在多线程的情况下线程安全也不会有性能问题。
静态内部类单例模式中实例由内部类创建,由于JVM在加载外部类的过程中,是不会加载静态内部类的,只有内部类的属性/方法被调用时才会被加载,并初始化其静态属性。静态属性由于被static修饰,保证只被实例化一次,并且严格保证实例化顺序。
public class Singleton{
private Singleton(){}
private static class SingletonHolder{
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
说明:
1. 第一次加载Singleton类时不会去初始化INSTANCE,只有第一次调用getInstance, 虚拟机加载SingletonHolder并初始化INSTANCE, 这样不仅能确保线程安全,也能保证Singleton类的唯一性。
2. 静态内部类单例模式是一种优秀的单例模式,是开源项目中比较常用的一种单例模式。在没有任何锁的情况下,保证了多线程下的安全,并且没有任何性能影响和空间的浪费。
破坏单例模式:
使上面定义的单例类(Singleton)可以创建多个多个对象,枚举方式除外。有两种方式,分别是序列化和反射。
import java.io.*;
class Singleton implements Serializable {
private Singleton(){}
private static class SingleHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance(){
return SingleHolder.INSTANCE;
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
Singleton instance = Singleton.getInstance();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("F:\\cs.txt"));
oos.writeObject(instance);
oos.close();
ObjectInputStream ios = new ObjectInputStream(new FileInputStream("F:\\cs.txt"));
Singleton instance2 = (Singleton) ios.readObject();
ios.close();
System.out.println(instance == instance2);
}
}
上面代码运行结果是false, 表明序列化和反序列化已经破坏了单例设计模式。
import java.io.*;
import java.lang.reflect.Constructor;
class Singleton {
private Singleton(){}
private static class SingleHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance(){
return SingleHolder.INSTANCE;
}
public static void main(String[] args) throws Exception {
//1.获取Singleton的字节码对象
Class clazz = Singleton.class;
//2.获取无参构造方法对象
Constructor cons = clazz.getDeclaredConstructor();
//3.取消访问检查
cons.setAccessible(true);
//4.创建Singleton对象
Singleton s1 = (Singleton) cons.newInstance();
Singleton s2 = (Singleton) cons.newInstance();
System.out.println(s1 == s2);
}
}
上面代码运行结果是false, 表明序列化和反序列化已经破坏了单例设计模式。
import java.io.*;
import java.lang.reflect.Constructor;
class Singleton implements Serializable {
private Singleton(){}
private static class SingleHolder{
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance(){
return SingleHolder.INSTANCE;
}
//当进行反序列化时,会自动调用该方法,将该方法的返回值直接返回+
public Object readResolve(){
return SingleHolder.INSTANCE;
}
public static void main(String[] args) throws Exception {
Singleton instance = Singleton.getInstance();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("F:\\cs.txt"));
oos.writeObject(instance);
oos.close();
ObjectInputStream ios = new ObjectInputStream(new FileInputStream("F:\\cs.txt"));
Singleton instance2 = (Singleton) ios.readObject();
ios.close();
System.out.println(instance == instance2);//true
}
}
import java.io.*;
import java.lang.reflect.Constructor;
class Singleton {
private static boolean flag = false;
private Singleton(){
synchronized (Singleton.class){
if(flag){
throw new RuntimeException("对象已经创建");
}
flag = true;
}
}
public static void main(String[] args) throws Exception {
//1.获取Singleton的字节码对象
Class clazz = Singleton.class;
//2.获取无参构造方法对象
Constructor cons = clazz.getDeclaredConstructor();
//3.取消访问检查
cons.setAccessible(true);
//4.创建Singleton对象
Singleton s1 = (Singleton) cons.newInstance();
Singleton s2 = (Singleton) cons.newInstance();
System.out.println(s1 == s2);
}
}
Runtime类就是使用的单例设计模式
[1].通过源代码查看使用的是哪儿种单例模式
package java.lang;
import java.io.*;
import java.util.StringTokenizer;
import sun.reflect.CallerSensitive;
import sun.reflect.Reflection;
public class Runtime {
private static Runtime currentRuntime = new Runtime();
public static Runtime getRuntime() {
return currentRuntime;
}
private Runtime() {}
public void exit(int status) {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkExit(status);
}
Shutdown.exit(status);
}
public void halt(int status) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkExit(status);
}
Shutdown.halt(status);
}
@Deprecated
public static void runFinalizersOnExit(boolean value) {
SecurityManager security = System.getSecurityManager();
if (security != null) {
try {
security.checkExit(0);
} catch (SecurityException e) {
throw new SecurityException("runFinalizersOnExit");
}
}
Shutdown.setRunFinalizersOnExit(value);
}
public Process exec(String command) throws IOException {
return exec(command, null, null);
}
public Process exec(String command, String[] envp) throws IOException {
return exec(command, envp, null);
}
public Process exec(String command, String[] envp, File dir)
throws IOException {
if (command.length() == 0)
throw new IllegalArgumentException("Empty command");
StringTokenizer st = new StringTokenizer(command);
String[] cmdarray = new String[st.countTokens()];
for (int i = 0; st.hasMoreTokens(); i++)
cmdarray[i] = st.nextToken();
return exec(cmdarray, envp, dir);
}
public Process exec(String cmdarray[]) throws IOException {
return exec(cmdarray, null, null);
}
public Process exec(String[] cmdarray, String[] envp, File dir)
throws IOException {
return new ProcessBuilder(cmdarray)
.environment(envp)
.directory(dir)
.start();
}
public native int availableProcessors();
public native long totalMemory();
public native void gc();
private static native void runFinalization0();
public void runFinalization() {
runFinalization0();
}
public native void traceMethodCalls(boolean on);
@CallerSensitive
public void load(String filename) {
load0(Reflection.getCallerClass(), filename);
}
synchronized void load0(Class<?> fromClass, String filename) {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkLink(filename);
}
if (!(new File(filename).isAbsolute())) {
throw new UnsatisfiedLinkError(
"Expecting an absolute path of the library: " + filename);
}
ClassLoader.loadLibrary(fromClass, filename, true);
}
@CallerSensitive
public void loadLibrary(String libname) {
loadLibrary0(Reflection.getCallerClass(), libname);
}
synchronized void loadLibrary0(Class<?> fromClass, String libname) {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkLink(libname);
}
if (libname.indexOf((int)File.separatorChar) != -1) {
throw new UnsatisfiedLinkError(
"Directory separator should not appear in library name: " + libname);
}
ClassLoader.loadLibrary(fromClass, libname, false);
}
@Deprecated
public InputStream getLocalizedInputStream(InputStream in) {
return in;
}
public OutputStream getLocalizedOutputStream(OutputStream out) {
return out;
}
}
从上面源代码中可以看出Runtime类使用的是饿汉式(静态属性)方式实现单例模式
使用Runtime类
package com.xiaotian;
import java.io.IOException;
import java.io.InputStream;
public class RuntimeDemo {
public static void main(String[] args) throws IOException {
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec("ipconfig");
InputStream is = process.getInputStream();
byte[] arr = new byte[1024*1024];
int len = -1;
while((len = is.read(arr)) != -1){
System.out.println(new String(arr, 0, len, "GBK"));
}
}
}
需求:设计一个咖啡店点餐系统。
设计一个咖啡类(Coffee), 并定义其两个子类(美式咖啡[AmericanCooffee])和拿铁咖啡[LatteCoffee]);再设计一个咖啡店类(CoffeeStore), 咖啡店具有点咖啡的功能。
具体类的设计如下:
代码如下:
public abstract class Coffee{
public abstract String getName();
public void addSugar(){
System.out.println("加糖");
}
public void addMilk(){
System.out.println("添加牛奶");
}
}
public class AmericanCoffee extends Coffee{
public String getName(){
return "美式咖啡";
}
}
public class LatteCoffee extends Coffee{
public String getName(){
return "拿铁咖啡";
}
}
public class CoffeeStore{
public Coffee orderCoffee(String type){
Coffee coffee = null;
if("american".equals(type)){
coffee = new AmericanCoffee();
}else if("latte".equals(type)){
coffee = new LatteCoffee();
}else{
throw new RuntimeException("对不起,你所点的咖啡没有");
}
//加配料
coffee.addMilk();
coffee.addSugar();
return coffee;
}
}
public class Client{
public static void main(String[] args){
CoffeeStore store = new CoffeeStore();
Coffee coffee = store.orderCoffee("latte");
System.out.println(coffee.getName());
}
}
在java中,万物皆对象,这些对象都需要创建,如果创建的时候直接new该对象,就会对该对象耦合严重,假如我们要更换对象,所有new对象的地方都需要修改一遍,这显然违背了软件设计的开闭原则。如果我们使用工厂来生产对象,我们就只和工厂打交道就可以了,彻底和对象解耦,如果要更换对象,直接在工厂里更换该对象即可,达到了与对象解耦的目的;所以说,工厂模式最大的优点就是:解耦。
最常用的工厂模式有三种:
简单工厂不是一种设计模式,反而比较像是一种编程习惯。
简单工厂包含如下角色:
public abstract class Coffee{
public abstract String getName();
public void addMilk(){
System.out.println("添加牛奶");
}
public void addSugar(){
System.out.println("添加糖");
}
}
public class AmericanCoffee extends Coffee{
public String getName(){
return "美式咖啡";
}
}
public class LatteCoffee extends Coffee{
public String getName(){
return "拿铁咖啡";
}
}
public class SimpleCoffeeFactory{
public Coffee createCoffee(String type){
Coffee coffee = null;
if("american".equals(type)){
coffee = new AmericanCoffee();
}else if("latte".equals(type)){
coffee = new LatteCoffee();
}else{
throw new RuntimeException("对不起,你所点的咖啡没有");
}
return coffee;
}
}
public class CoffeeStore{
public Coffee orderCoffee(String type){
SimpleCoffeeFactory factory = new SimpleCoffeeFactory();
Coffee coffee = factory.createCoffee(type);
coffee.addMilk();
coffee.addSugar();
return coffee;
}
}
public class Client{
public static void main(String[] args){
CoffeeStore store = new CoffeeStore();
Coffee coffee = store.orderCoffee("latte");
System.out.println(coffee.getName());
}
}
工厂(factory)处理创建对象的细节,一旦有了SimpleCoffeeFactory, CoffeeStore类中的orderCoffee()就变成此对象的客户,后期如果需要Coffee对象直接从工厂中获取即可。这样也解除了和Coffee实现类的耦合,同时又产生了新的耦合,CoffeeStore对象SimpleCoffeeFactory工厂对象耦合,工厂对象和商品对象的耦合。
后期如果如果再添加新品种的咖啡,我们势必要需求修改SimpleCoffeeFactory的代码,违反了开闭原则。工厂类的客户端可能有很多,比如创建美团外卖等,这样只需要修改工厂类的代码,省去其他的修改操作。
封装了创建对象的过程,可以通过参数直接获取对象。把对象的创建和业务逻辑层分开,这样以后避免了修改客户代码,如果实现新产品直接修改工厂类,而不需要在原代码中修改的可能性,更加容易扩展。
增加新产品时还是需要修改工厂类的代码,违背了"开闭原则"。
在开发中也有一部分人将工厂类中的创建对象的功能定义为静态的,这个就是静态工厂模式,它也不是23种设计模式。代码如下:
public class SimpleCoffeeFactory{
public static Coffee createCoffee(String type){
Coffee coffee = null;
if("americano".equals(type)){
coffee = new AnericanoCoffee();
}else if("latte".equals(type)){
coffee = new LatteCoffee();
}
return coffee;
}
}
针对上例中的缺点,使用工厂方法就可以完美的解决,完全遵循开闭原则。
定义一个用于创建对象的接口,让子类决定实例化哪个产品类对象。工厂方法是一个产品类的实例化延迟到其工厂的子类。
工厂方法模式的主要角色:
public abstract Coffee{
public abstract String getName();
public void addMilk(){
System.out.println("添加牛奶");
}
public void addSugar(){
System.out.println("添加糖");
}
}
public class AmerianoCoffee extends Coffee{
public String getName(){
return "美式咖啡";
}
}
public class LatteCoffee extends Coffee{
public String getName(){
return "拿铁咖啡";
}
}
public interface CoffeeFactory{
Coffee createCoffee();
}
public class AmericanoCoffeeFactory implements CoffeeFactory{
public Coffee createCoffee(){
return new AmericanoCoffee();
}
}
public class LatteCoffeeFactory implements CoffeeFactory{
public Coffee createCoffee(){
return new LatteCoffee();
}
}
public class CoffeeStore{
private CoffeeFactory factory;
public void setFactory(CoffeeFactory factory){
this.factory = factory;
}
public Coffee orderCoffee(){
Coffee coffee = factory.createCoffee();
coffee.addMilk();
coffee.addSugar();
return coffee;
}
}
public class Client{
public static void main(String[] args){
CoffeeStore store = new CoffeeStore();
CoffeeFactory factory = new AmericanCoffeeFactory();
store.setFactory(factory);
Coffee coffee = store.orderCoffee();
System.out.println(coffee.getName());
}
}
从以上的编写的代码可以看到,要增加产品类时也要相应地增加工厂类,不需要修改工厂类的代码,这样就解决了简单工厂模式的缺点。
工厂方法模式是简单工厂模式的进一步抽象。由于使用了多态性,工厂方法模式保持了简单工厂模式的有点,而且克服了它的缺点。
前面介绍的工厂方法模式中考虑的是一类产品的生产,如畜牧场只养动物,电视机厂只生产电视等。
这些工厂只生产同种类产品,同种类产品称为同等级产品,也就是说:工厂方法模式只考虑生产同等级的产品,但是在现实生活中许多工厂是综合型工厂,能生产多等级(种类)的产品,如电器厂既生产电视机又生产洗衣机或空调,大学既有
抽象工厂模式将考虑多等级产品的生产,将同一个具体工厂所生产的位于不同等级的一组产品称为一个产品族,下图所示横轴是产品等级,也就是同一类产品;纵轴是产品,也就是同一品牌的产品,同一品牌的产品产自同一个工厂。
是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。
抽象工厂模式是工程方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可身材多个等级的产品。
抽象工厂模式的主要角色如下:
现在咖啡业务发生改变,不仅要生产咖啡还要生产甜点,如提拉米苏,抹茶慕斯等,要是按照工厂模式,需要创建提拉米苏,抹茶慕斯类,提拉米苏工厂,抹茶慕斯工厂,甜点工厂类,很容易发生类爆炸。其中拿铁咖啡,美式咖啡是一个产品等级都是咖啡,提拉米苏,抹茶慕斯又是一个产品等级;拿铁咖啡和提拉米苏是同一产品族(也就是都属于意大利风味),美式咖啡和抹茶慕斯是同一产品族(也就是美式风味)。所以这个案例可以使用抽象工厂模式实现,类图如下:
代码如下:
public abstract class Coffee{
public abstract String getName();
public void addMilk(){
System.out.println("添加牛奶");
}
public void addSugar(){
System.out.println("添加糖");
}
}
public class AmericanoCoffee extends Coffee{
public String getName(){
return "美式咖啡";
}
}
public class LatteCoffee extends Coffee{
public String getName(){
return "拿铁咖啡";
}
}
public abstract class Dessert{
public abstract void show();
}
public class Tiramisu extends Dessert{
public void show(){
System.out.println("提拉米苏");
}
}
public class MatchaMouse extends Dessert{
public void show(){
System.out.println("抹茶慕斯");
}
}
public interface DessertFactory{
public Coffee createCoffee();
public Dessert createDessert();
}
public class AmericanoDessertFactory implements DessertFactory{
@Override
public Coffee createCoffee(){
return new AmericanCoffee();
}
@Override
public Dessert createDessert(){
return new MatchaMouse();
}
}
public class ItalyDessertFactory implements DessertFactory{
@Override
public Coffee createCoffee(){
return new LatteCoffee();
}
@Override
public Dessert createDessert(){
return new Tiramisu();
}
}
public class Client{
public static void main(String[] args){
AmericanDessertFactory factory = new AmericanDessertFactory();
Coffee coffee = factory.createCoffee();
Dessert dessert = factory.createDessert();
System.out.println(coffee.getName());
dessert.show();
}
}
如果要加同一个产品族的话,只需要在加一个对应的工厂类即可,不需要修改其他的类。
当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只能使用同一个产品族中的对象。
当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。
可以通过工厂模式+配置文件的方式解除工厂对象和产品对象的耦合。在工厂类中加载配置文件中的全类名,并创建对象进行存储,客户端如果需要对象,直接获取即可。
示例:
[1].创建配置文件bean.properties
american = com.xiaotian.AmericanCoffee
latte = com.xiaotian.LatteCoffee
[2].创建Coffee类,AmericanCoffee类和LatterCoffee类
package com.xiaotian;
public abstract class Coffee {
public abstract String getName();
public void addMilk(){
System.out.println("添加牛奶");
}
public void addSugar(){
System.out.println("添加糖");
}
}
package com.xiaotian;
public class AmericanCoffee extends Coffee{
@Override
public String getName() {
return "美式咖啡";
}
}
package com.xiaotian;
public class LatteCoffee extends Coffee{
@Override
public String getName() {
return "拿铁咖啡";
}
}
[3].改进工厂类
package com.xiaotian;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
public class CoffeeFactory {
private static Map<String, Coffee> map = new HashMap<>();
static{
try {
Properties p = new Properties();
InputStream is = CoffeeFactory.class.getClassLoader().getResourceAsStream("bean.properties");
p.load(is);
Set<Object> keys = p.keySet();
for (Object key : keys) {
String className = p.getProperty((String) key);
Class clzz = Class.forName(className);
Coffee coffee = (Coffee) clzz.newInstance();
map.put((String) key, coffee);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static Coffee createCoffee(String name){
return map.get(name);
}
public static void main(String[] args) throws IOException {
System.out.println(CoffeeFactory.createCoffee("latte"));
}
}
[4].测试类
public class Client{
public static void main(String[] args){
System.out.println(CoffeeFactory.createCoffee("latte"));
}
}
public class Demo{
public static void main(String[] args){
List<String> list = new ArrayList();
list.add("令狐冲");
list.add("风清扬");
list.add("任我行");
//获取迭代器对象
Iterator<String> it = list.iterator();
//使用迭代器遍历
while(it.hasNext()){
String ele = it.next();
System.out.println(ele);
}
}
}
使用迭代器遍历集合,获取集合中的元素。而单列集合集合获取迭代器就使用到了工厂方法模式。如下类图所示
Collection接口是抽象工程类,ArrayList是具体的工厂类;Iterator接口是抽象商品类,ArrayList类中的Iter内部类是具体的商品类。在具体的工厂类中iterator()方法创建具体的商品类的对象
另外:
1. DateFormat类中的getInstance()方法使用的是工厂模式
2. Calendar类中的getInstance()方法使用的是工厂模式
用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象。
原型模式包含如下角色:
原型模式的克隆可分为浅克隆和深克隆
浅克隆: 创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原来属性所指向的对象的内存地址。
深克隆: 创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有地址对象
Java中的Object类中提供了clone()方法来实现浅克隆。Cloneablej接口是上面的类图中的抽象原型类,而实现了Cloneable接口的子实现类就是具体的原型类。
示例
public class RealizeType implements Cloneable{
public RealizeType() {
}
@Override
protected RealizeType clone() throws CloneNotSupportedException {
return (RealizeType) super.clone();
}
public static void main(String[] args) throws CloneNotSupportedException {
RealizeType r1 = new RealizeType();
RealizeType r2 = r1.clone();
System.out.println(r1 == r2);//false
}
}
用原型模式生成"三好学生"奖状
同一学校的"三好学生"奖状除了获奖人姓名不同,其他都相同,可以使用原型模式复制多个"三好学生"奖状出来,然后再修改奖状上的名字即可。
类图如下:
代码如下:
public class Citation implements Cloneable{
private String name;
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public void show(){
System.out.println(name+"被评为三好学生");
}
@Override
protected Citation clone() throws CloneNotSupportedException {
return (Citation) super.clone();
}
}
public class CitationTest{
public static void main(String[] args){
Citation c1 = new Citation();
c1.setName("张三");
Citation c2 = c1.clone();
c2.setName("李四");
c1.show();
c2.show();
}
}
示例
将上面的"三号学生"奖状的案例中Citation类的name属性修改为Student类型的属性。代码如下:
package com.xiaotian;
public class Student {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}';
}
}
package com.xiaotian;
public class Citation implements Cloneable{
private Student student;
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
@Override
public String toString() {
return "Citation{" +
"student=" + student +
'}';
}
@Override
protected Citation clone() throws CloneNotSupportedException {
return (Citation) super.clone();
}
public static void main(String[] args) throws CloneNotSupportedException {
Student student = new Student();
student.setName("张三");
Citation c1 = new Citation();
c1.setStudent(student);
Citation c2 = c1.clone();
System.out.println(c1);
System.out.println(c2);
System.out.println(c1 == c2);
}
}
将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示。
建造者(Builder)模式包含如下角色:
创建共享单车
生产自行车是一个复杂的过程,它包含了车架,车座等组件的生产。而车架又有炭纤维,铝合金等材质的,车座有橡胶,真皮等材质。对于自行车的生产就可以使用建造者模式。
这里Bike是产品,包含车架,车座等组件;Builder是抽象建造者,MobikeBuilder和OfoBuilder是具体的建造者;Director是指挥者。类图如下:
具体的代码如下:
public class Bike{
private String frame; //车架
private String seat; //车座
public String getFrame(){
return frame;
}
public void setFrame(String frame){
this.frame = frame;
}
public String getSeat(){
return seat;
}
public void setSeat(String seat){
this.seat = seat;
}
}
public abstract class Builder{
//声明Bike类型的变量,并进行赋值
protected Bike bike = new Bike();
public abstract void buildFrame();
public abstract void buildSeat();
//构建自行车的方法
public abstract Bike createBike();
}
public class MobileBuilder extends Builder{
@Override
public void buildFrame(){
bike.setFrame("碳纤维车架");
}
@Override
public void buildSeat(){
bike.setSeat("真皮座椅");
}
@Override
public Bike createBike(){
return bike;
}
}
public class OfoBuilder extends Builder{
@Override
public void buildFrame(){
bike.setFrame("铝合金车架");
}
@Override
public void buildSeat(){
bike.setSeat("橡皮车座");
}
@Override
public Bike createBike(){
return bike;
}
}
public class Director{
//声明builder类型变量
private Builder builder;
public Director(Builder builder){
this.builder = builder;
}
//组装自行车功能
public Bike construct(){
builder.buildFrame();
builder.buildSeat();
return builder.createBike();
}
}
public class Client{
public static void main(String[] args){
//创建指挥者对象
Director director = new Director(new MobileBuilder());
//让指挥者会组装自行车
Bike bike = director.construct();
System.out.println(bike.getFrame());
System.out,println(bike.getSeat());
}
}
上面示例是Builder模式的常规用法,指挥类Director在建造者模式中具有很重要的作用,它用于指导具体构建这如何构建产品,控制调用先后次序,并向调用者返回完整的产品类,但是有些情况下需要简化系统结构,可以把指挥者类和抽象建造者进行结合
//抽象builder类
public abstract class Builder{
protected Bike mBike = new Bike();
public abstract void buildFrame();
public abstract void buildSeat();
public abstract Bike createBike();
public Bike construct(){
this.buildFrame();
this.buildSeat();
return this.createBike();
}
}
说明
这样做确实简化了系统结构,但同时也加重了抽象建造者类的职责,也不是太符合单一职责原则,如果construct()过于复杂,建议还是封装到Director中。
建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
建造者(Builder)模式创建的是复杂对象,其产品的各个部分经常面临剧烈的变化,但将它们组合在一起的算法却相对稳定,所以它通常在以下场合使用。
建造者模式除了上面的用途外,在开发中还有一个常用的使用方式,就是当一个类构造器需要传入多个参数时,如果创建这个类的实例,代码可读性非常差,而且很容易引入错误,此时就可以利用建造者模式进行重构。
重构前代码如下:
public class Phone{
private String cpu;
private String screen;
private String memory;
private String mainboard;
public Phone(String cpu, String screen, String memory, String mainboard){
this.cpu = cpu;
this.screen = screen;
this.memory = memory;
this.mainboard = mainboard;
}
public String getCpu(){
return cpu;
}
public void setCpu(String cpu){
this.cpu = cpu;
}
public String getScreen(){
return screen;
}
public void setScreen(String screen){
this.screen = screen;
}
public String getMemory(){
return memory;
}
public void setMemory(String memory){
this.memory = memory;
}
public String getMainboard(){
return mainboard;
}
public void setMainboard(String mainboard){
this.mainboard = mainboard;
}
}
public class Client{
public static void main(String[] args){
//构建Phone对象
Phone phone = new Phone("intel", "王星屏幕", "金士顿", "华硕");
System.out.println(phone);
}
}
上面在客户端代码中构建Phone对象,传递了四个参数,如果参数更多呢?代码的可读性及使用的成本就是比较高。
重构后代码:
public class Phone{
private String cpu;
private String screen;
private String memory;
private String mianboard;
private Phone(Builder builder){
this.cpu = builder.cpu;
this.screen = builder.screen;
this.memory = builder.memory;
this.mainboard = builder.mainboard;
}
public static final class Builder{
private String cpu;
private String screen;
private String memory;
private String mainboard;
public Builder cpu(String cpu){
this.cpu = cpu;
return this;
}
public Builder screen(String screen){
this.screen = screen;
return this;
}
public Builder memory(String memory){
this.memory = memory;
return this;
}
public Builder mainboard(String mainboard){
this.mainboard = mainboard;
return this;
}
public Phone build(){
return new Phone(this);
}
}
}
public class Client{
public static void main(String[] args){
Phone phone = new Phone.Builder()
.cpu("intel")
.screen("三星屏幕")
.memory("金士顿内存条")
.mainboard("华硕主板")
.build();
System.out.println(phone);
}
}
工厂方法模式注重的是整体对象的创建方式;而建造者模式注重的是部件构建的过程,意在通过一步一步地精确构造创建出一个复杂的对象。
例如:要制造一个超人,如果使用工厂方法模式,直接产生出的就是一个力大无穷,能够飞翔,内裤外穿的超人;而如果使用建造者模式,则需要组装手,头,脚,躯干等部分,然后再把内裤外穿,于是一个超人就诞生了。
抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品: 具有不同分类纬度的产品组合,采用抽象工厂模式则是不需要关心构建过程,只关心什么产品由什么工厂生产即可。
建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品。
如果将抽象工厂模式看成汽车配件生产厂,生成一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车。