设计模式(一)
设计模式
[TOC]
创建型模式
工厂方法模式
工厂方法模式的用意是定义一个创建产品对象的工厂接口,将实际创建性工作推迟到子类中。
定义一个用于创建对象的接口,让子类决定实例化哪个类。工厂方法使一个类的实例化延迟到其子类。
四个角色
抽象工厂(Creator)角色
该角色是工厂方法模式的核心,与应用系统无关,任何在创建对象的工厂类必须实现这个接口
/**
* 抽象工厂(Creator)角色
*
* @author Jenson
*/
public interface Creator {
/**
* 创建一个产品对象,输入其参数类型可以自行设置
*
* @param c
* @param <T>
* @return
*/
public <T extends Product> T factory(Class<T> c);
}
具体工厂(Concrete Creator)角色
该角色实现了抽象工厂接口,含有与应用密切相关的逻辑,并且受到应用程序的调用以创建产品对象。
具体生产何种产品对象是由不同的工厂类决定的
/**
* 具体工厂(Concrete Creator)角色
*
* @author Jenson
*/
public class ConcreteCreator implements Creator {
public <T extends Product> T factory(Class<T> c) {
Product product = null;
try {
product = (Product) Class.forName(c.getName()).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return (T) product;
}
}
抽象产品(Product)角色
该角色负责定义产品的共性,实现对产品最抽象的定义
/**
* 抽象产品(Product)角色
*
* @author Jenson
*/
public interface Product {
/**
* 产品类公共方法
*/
void doSomething();
}
具体产品(Concrete Product)角色
该角色实现抽象产品角色所声明的接口,工厂方法模式所创建的每一个对象都是某个具体产品角色的实例
/**
* 具体产品(Concrete Product)角色
*
* @author Jenson
*/
public class ConcreteProduct implements Product {
public void doSomething() {
System.out.println("这是一个具体的产品,我们可以称它为桌子,或其他什么东西");
}
}
调用:
/**
* @author Jenson
*/
public class ProductMain {
public static void main(String[] args) {
// 实现一个具体的工厂
Creator creator = new ConcreteCreator();
// 往工厂中装载一个具体的产品
Product product = creator.factory(ConcreteProduct.class);
product.doSomething();
}
}
抽象工厂模式
为创建一组<font color=red>相关或相互依赖</font>的对象提供一个接口,而且无须指定它们的具体类
使用场景
当一个对象族(或是一组没有任何关系的对象)都有相同的约束,则可以使用抽象工厂模式
四个角色
抽象产品(Abstract Product)角色
该角色负责定义产品的共性,实现对产品最抽象的定义。
/**
* 抽象产品(Abstract Product)角色
* 游戏装备--坐骑
*
* @author Jenson
*/
public interface Mounts {
/**
* 坐骑公共方法--奔驰
*/
public void gallop();
}
/**
* 抽象产品(Abstract Product)角色
* 游戏装备--武器
*
* @author Jenson
*/
public interface Weapon {
/**
* 武器公共方法--进攻
*/
public void attack();
}
具体产品(Concrete Product)角色
该角色实现抽象产品角色所声明的接口,抽象工厂模式所创建的任何产品对象都是某个具体产品角色的实例
/**
* 具体产品(Concrete Product)角色
* 古代坐骑
*
* @author Jenson
*/
public class Horse implements Mounts {
public void gallop() {
System.out.println("小马驹抬起它可爱的四只脚向前奔驰,一不开心它就尥蹶子");
}
}
/**
* 具体产品(Concrete Product)角色
* 现代坐骑
*
* @author Jenson
*/
public class Car implements Mounts {
public void gallop() {
System.out.println("小汽车跑得可比马快多了,可惜古代没有加油站");
}
}
/**
* 具体产品(Concrete Product)角色
* 古代武器
*
* @author Jenson
*/
public class Knife implements Weapon {
public void attack() {
System.out.println("一刀999,狗带吧");
}
}
/**
* 具体产品(Concrete Product)角色
* 现代武器
*
* @author Jenson
*/
public class Gun implements Weapon {
public void attack() {
System.out.println("突!突!突!突!突!突!突!");
}
}
抽象工厂(Abstract Factory)角色
该角色是抽象工厂模式的核心,与应用系统无关,任何创建对象的工厂类必须实现这个接口。
/**
* 抽象工厂(Abstract Factory)角色
* 游戏装备工厂
*
* @author Jenson
*/
public interface GameEquipmentFactory {
/**
* 创建装备--坐骑
*
* @return 坐骑
*/
public Mounts mounts();
/**
* 创建装备--武器
*
* @return 武器
*/
public Weapon weapon();
}
具体工厂(Concrete Factory)角色
该角色实现了抽象工厂接口,含有选择合适的产品对象的逻辑,并且受到应用程序的调用以创建产品对象。
/**
* 具体工厂(Concrete Factory)角色
* 古代装备工厂
*
* @author Jenson
*/
public class AncientEquipmentFactory implements GameEquipmentFactory {
public Mounts mounts() {
return new Horse();
}
public Weapon weapon() {
return new Knife();
}
}
/**
* 具体工厂(Concrete Factory)角色
* 现代装备工厂
*
* @author Jenson
*/
public class ModernEquipmentFactory implements GameEquipmentFactory {
public Mounts mounts() {
return new Car();
}
public Weapon weapon() {
return new Gun();
}
}
调用:
/**
* @author Jenson
*/
public class FactoryMain {
public static void main(String[] args) {
// 创建两个工厂
// 古代工厂
GameEquipmentFactory ancientFactory = new AncientEquipmentFactory();
// 生产一个古代坐骑
Mounts ancientMounts = ancientFactory.mounts();
ancientMounts.gallop();
// 生产一个古代武器
Weapon ancientWeapon = ancientFactory.weapon();
ancientWeapon.attack();
//现代工厂
GameEquipmentFactory modernFactory = new ModernEquipmentFactory();
// 生产一个现代坐骑
Mounts modernMounts = modernFactory.mounts();
modernMounts.gallop();
// 生产一个现代武器
Weapon modernWeapon = modernFactory.weapon();
modernWeapon.attack();
}
}
建造者模式
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
使用场景
建造者模式关注的是<font color=red>零件类型和装配工艺顺序</font>,这是与工厂方法模式最大的不同之处,虽然同为创建类模式,但是重点不同
四个角色
产品(Product)角色
该角色是建造中的复杂对象,一个系统中会有多于一个的产品类,这些产品类并不一定有共同的接口,完全可以是不相关联的
/**
* 产品抽象类
*
* @author Jenson
*/
@Data
public abstract class BaseComputer {
/**
* 型号
*/
private String type;
/**
* 操作系统
*/
private String os;
}
/**
* 产品(Product)角色
*
* @author Jenson
*/
@Data
public class MacPro extends BaseComputer {
public MacPro() {
this.setType("苹果电脑");
}
/**
* 型号
*/
private String type;
@Override
public String toString() {
return "Type:" + this.getType() + " OS:" + this.getOs();
}
}
/**
* 产品(Product)角色
*
* @author Jenson
*/
@Data
public class MatePro extends BaseComputer {
public MatePro() {
this.setType("华为电脑");
}
/**
* 型号
*/
private String type;
@Override
public String toString() {
return "Type:" + this.getType() + " OS:" + this.getOs();
}
}
抽象建造者(Builder)角色
该角色用于规范产品的各个组成部分,并进行抽象,一般独立于应用程序的逻辑
/**
* 抽象建造者(Builder)角色
*
* @author Jenson
*/
public interface ComputerBuilder {
/**
* 安装系统
*/
void installOs();
/**
* 组建好的产品实例
* 获得构造好的电脑
*
* @return 构造好的电脑
*/
BaseComputer getComputer();
}
具体建造者(Concrete Builder)角色
该角色实现抽象建造者中定义的所有方法,并且返回一个组建好的产品实例
/**
* 具体建造者(Concrete Builder)角色
*
* @author Jenson
*/
public class MacProComputerBuilder implements ComputerBuilder {
private MacPro macPro = new MacPro();
public void installOs() {
macPro.setOs("Mac OsX");
}
public MacPro getComputer() {
return macPro;
}
}
/**
* 具体建造者(Concrete Builder)角色
*
* @author Jenson
*/
public class MateProComputerBuilder implements ComputerBuilder {
private MatePro matePro = new MatePro();
public void installOs() {
matePro.setOs("Windows10 X86");
}
public MatePro getComputer() {
return matePro;
}
}
导演者(Director)角色
该角色负责安排已有模块的顺序,然后告诉 Builder 开始建造
导演者Director类起到封装作用,避免高层模块深入到建造者内部的实现类。导演类可以有多个,根据项目的实际情况来决定使用的个数
/**
* 导演者(Director)角色
*
* @author Jenson
*/
public class ComputerDirector {
private ComputerBuilder builder;
/**
* 指导构造计算机的动作
*
* @param c 构造的产品类
* @param <T> 构造的产品类
* @return 构造好的产品
*/
public <T extends BaseComputer> T constuctComputer(Class<T> c) {
try {
builder = (ComputerBuilder) Class.forName(c.getName() + "ComputerBuilder").newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
builder.installOs();
return (T) builder.getComputer();
}
}
调用:
/**
* @author Jenson
*/
public class BuilderMain {
public static void main(String[] args) {
ComputerDirector director = new ComputerDirector();
System.out.println("================== build mac pro=================");
System.out.println(director.constuctComputer(MatePro.class));
System.out.println("================== build mate pro=================");
System.out.println(director.constuctComputer(MacPro.class));
}
}
原型模式
用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象
在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过clone()方法创建一个对象,然后由工厂方法提供给调用者
使用场景
1、资源优化场景:原型模式是在内存二进制流的复制,要比直接new一个对象性能好,特别是在一个循环体内产生大量的对象时,原型模式可以更好地体现其优点
2、性能和安全要求的场景:通过new产生一个对象需要非常烦琐的数据准备或访问权限,可以使用原型模式
3、一个对象多个修改者的场景:一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式复制多个对象供调用者使用。
需要注意的是:Java中Object提供的clone()方法采用的是“浅”克隆,即只复制关联对象的引用,而不复制关联对象的数据。如果需要“深”克隆,则需要在覆盖clone()方法时手动控制克隆的深度
四个角色
客户(Client)角色
该角色提出创建对象的请求
/**
* 客户(Client)角色
*
* @author Jenson
*/
public class Client {
public void operation(Prototype example) {
Prototype p = example.clone();
}
}
抽象原型(Prototype)角色
该角色是一个抽象角色,通常由一个Java接口或抽象类实现,给出所有的具体原型类所需的接口
/**
* 抽象原型(Prototype)角色
* <p>
* Java中内置了克隆机制,
* 只需要让抽象原型Prototype接口继承 Cloneable 接口,
* 以标明该接口的实现类可以被复制,并声明一个 clone()方法
*
* @author Jenson
*/
public interface Prototype extends Cloneable {
/**
* 克隆方法
*
* @return 克隆对象
*/
Prototype clone();
}
具体原型(Concrete Prototype)角色
该角色是被复制的对象,必须实现抽象原型接口
/**
* 具体原型(Concrete Prototype)角色
*
* @author Jenson
*/
@Data
public class ConcretePrototype implements Prototype {
private String param1;
private String param2;
@Override
public Prototype clone() {
try {
return (Prototype) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
}
调用
/**
* @author Jenson
*/
public class PrototypeMain {
public static void main(String[] args) {
ConcretePrototype concretePrototype = new ConcretePrototype();
concretePrototype.setParam1("param01");
concretePrototype.setParam2("param02");
System.out.println(concretePrototype.toString());
ConcretePrototype concretePrototypeClone = (ConcretePrototype) concretePrototype.clone();
System.out.println(concretePrototypeClone.toString());
}
}
浅拷贝/深拷贝
浅拷贝原型
/**
* @author Jenson
*/
@Data
public class Mail implements Cloneable {
private String address;
private String subject;
private String content;
private AttachedFile attachedFile;
@Override
public Mail clone(){
try {
return (Mail) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
}
/**
* @author Jenson
*/
@Data
public class AttachedFile {
private String fileName;
private String fileUrl;
}
调用
/**
* @author Jenson
*/
public class MailCloneMain {
public static void main(String[] args) {
// 附件
AttachedFile file = new AttachedFile();
file.setFileName("煮地瓜.doc");
file.setFileUrl("http://jenson.com/files/煮地瓜.doc");
Mail mail = new Mail();
mail.setAddress("11111@qq.com");
mail.setSubject("浅拷贝");
mail.setContent("浅拷贝浅拷贝浅拷贝浅拷贝");
mail.setAttachedFile(file);
System.out.println(mail.toString());
// 拷贝
Mail mailClone = mail.clone();
mailClone.setAddress("222222@qq.com");
mailClone.getAttachedFile().setFileName("养猪攻略.doc");
System.out.println(mailClone.toString());
System.out.println(mail.toString());
}
}
输出
Mail(address=11111@qq.com, subject=浅拷贝, content=浅拷贝浅拷贝浅拷贝浅拷贝, attachedFile=AttachedFile(fileName=煮地瓜.doc, fileUrl=http://jenson.com/files/煮地瓜.doc))
Mail(address=222222@qq.com, subject=浅拷贝, content=浅拷贝浅拷贝浅拷贝浅拷贝, attachedFile=AttachedFile(fileName=养猪攻略.doc, fileUrl=http://jenson.com/files/煮地瓜.doc))
Mail(address=11111@qq.com, subject=浅拷贝, content=浅拷贝浅拷贝浅拷贝浅拷贝, attachedFile=AttachedFile(fileName=养猪攻略.doc, fileUrl=http://jenson.com/files/煮地瓜.doc))
小结
浅拷贝:被复制对象中的所拥有的对象仍然是指向原有对象
深拷贝原型
/**
* @author Jenson
*/
@Data
public class Mail implements Cloneable {
private String address;
private String subject;
private String content;
private AttachedFile attachedFile;
@Override
public Object clone() {
try {
Object obj = super.clone();
AttachedFile file = ((Mail) obj).getAttachedFile();
((Mail) obj).setAttachedFile((AttachedFile) file.clone());
return obj;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
}
/**
* @author Jenson
*/
@Data
public class AttachedFile implements Cloneable {
private String fileName;
private String fileUrl;
@Override
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
}
调用
/**
* @author Jenson
*/
public class MailCloneMain {
public static void main(String[] args) {
// 附件
AttachedFile file = new AttachedFile();
file.setFileName("煮地瓜.doc");
file.setFileUrl("http://jenson.com/files/煮地瓜.doc");
Mail mail = new Mail();
mail.setAddress("11111@qq.com");
mail.setSubject("深拷贝");
mail.setContent("深拷贝深拷贝深拷贝深拷贝");
mail.setAttachedFile(file);
System.out.println(mail.toString());
// 拷贝
Mail mailClone = (Mail) mail.clone();
mailClone.setAddress("222222@qq.com");
mailClone.getAttachedFile().setFileName("养猪攻略.doc");
System.out.println(mailClone.toString());
System.out.println(mail.toString());
}
}
输出
Mail(address=11111@qq.com, subject=深拷贝, content=深拷贝深拷贝深拷贝深拷贝, attachedFile=AttachedFile(fileName=煮地瓜.doc, fileUrl=http://jenson.com/files/煮地瓜.doc))
Mail(address=222222@qq.com, subject=深拷贝, content=深拷贝深拷贝深拷贝深拷贝, attachedFile=AttachedFile(fileName=养猪攻略.doc, fileUrl=http://jenson.com/files/煮地瓜.doc))
Mail(address=11111@qq.com, subject=深拷贝, content=深拷贝深拷贝深拷贝深拷贝, attachedFile=AttachedFile(fileName=煮地瓜.doc, fileUrl=http://jenson.com/files/煮地瓜.doc))
小结
要实现深拷贝,需要在被拷贝类的clone方法中实现所拥有的对象的拷贝
如果对象嵌套太深,要实现深拷贝则非常麻烦
深拷贝的实现方式
1、利用序列化实现深拷贝
序列化:
将Java对象转换成字节序列 ,这些字节序列可以被保存在磁盘上,或者通过网络传输,以备以后重新恢复成原来的对象
返序列化:
将字节序列转成java对象
2、需要序列化的类实现标识接口Serializable
/**
* @author Jenson
*/
@Data
public class Mail implements Cloneable, Serializable {
private static final long serialVersionUID = -5384854901543518881L;
private String address;
private String subject;
private String content;
private AttachedFile attachedFile;
@Override
public Object clone() {
try {
Object obj = super.clone();
AttachedFile file = ((Mail) obj).getAttachedFile();
((Mail) obj).setAttachedFile((AttachedFile) file.clone());
return obj;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
}
/**
* @author Jenson
*/
@Data
public class AttachedFile implements Cloneable, Serializable {
private static final long serialVersionUID = -6304480278476398078L;
private String fileName;
private String fileUrl;
@Override
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
}
注意,所需要序列化的对象所拥有的对象也需要实现序列化接口,否则会抛出java.io.NotSerializableException错误
Exception in thread "main" java.io.NotSerializableException: com.jenson.prototype.AttachedFile
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at com.jenson.prototype.DeepCloneUtils.deepClone(DeepCloneUtils.java:24)
at com.jenson.prototype.MailCloneMain.main(MailCloneMain.java:35)
3、编写深度拷贝工具函数
/**
* 使用序列化进行深拷贝
*
* @author Jenson
*/
public class DeepCloneUtils {
/**
* 利用序列化的方式实现对象拷贝
* @param src 原对象
* @param <T> 原对象类型
* @return 复制对象
* @throws IOException
* @throws ClassNotFoundException
*/
public static <T> T deepClone(T src) throws IOException, ClassNotFoundException {
Object obj;
// 在内存中创建一个字节数组缓冲区,默认32字节
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(src);
objectOutputStream.close();
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
obj = objectInputStream.readObject();
objectInputStream.close();
return (T) obj;
}
}
4、调用
/**
* @author Jenson
*/
public class MailCloneMain {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 附件
AttachedFile file = new AttachedFile();
file.setFileName("煮地瓜.doc");
file.setFileUrl("http://jenson.com/files/煮地瓜.doc");
Mail mail = new Mail();
mail.setAddress("11111@qq.com");
mail.setSubject("深拷贝");
mail.setContent("深拷贝深拷贝深拷贝深拷贝");
mail.setAttachedFile(file);
System.out.println(mail.toString());
// 拷贝
Mail mailClone = (Mail) mail.clone();
mailClone.setAddress("222222@qq.com");
mailClone.getAttachedFile().setFileName("养猪攻略.doc");
System.out.println(mailClone.toString());
System.out.println(mail.toString());
// 通过序列化实现深拷贝
System.out.println("============serializable deep clone============");
Mail maildeepClone = DeepCloneUtils.deepClone(mailClone);
maildeepClone.setAddress("333333@qq.com");
maildeepClone.getAttachedFile().setFileName("摩托车修理攻略.doc");
System.out.println(maildeepClone.toString());
System.out.println(mailClone.toString());
System.out.println(mail.toString());
}
}
2、???。。。
参考:
书名:设计模式:Java版 作者:青岛东合信息技术有限公司 出版社:电子工业出版社
https://blog.csdn.net/qq_33314107/article/details/80271963