设计模式(一)

2020-06-28  本文已影响0人  东南枝下

设计模式

[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

https://www.jianshu.com/p/16404803828f

https://www.cnblogs.com/zhangyongJava/p/10275416.html

上一篇下一篇

猜你喜欢

热点阅读