美文网首页
设计模式(一)

设计模式(一)

作者: 东南枝下 | 来源:发表于2020-06-28 14:56 被阅读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

    相关文章

      网友评论

          本文标题:设计模式(一)

          本文链接:https://www.haomeiwen.com/subject/xocwfktx.html