亲宝软件园·资讯

展开

设计模式 - 创建型模式详解

农夫三拳有点疼~ 人气:1
> 在软件工程中,**创建型模式**是处理对象创建的设计模式,试图根据实际情况使用合适的方式创建对象。基本的对象创建方式可能会导致设计上的问题,或增加设计的复杂度。创建型模式通过以某种方式控制对象的创建来解决问题。 > > 常用创建型模式有:单例模式、工厂模式、抽象工厂模式、原型模式、建造者模式 # 一、单例模式 ### 单例模式有以下8种写法: 1. 饿汉式: 1. 静态常量 2. 静态代码块 2. 懒汉式: 1. 线程不安全 2. 线程安全,同步方法 3. 线程安全,同步代码块 3. 双重检查 4. 静态内部类 5. 枚举 ### 单例模式的使用场景: 需要频繁创建和销毁的对象;创建时耗时过多或消耗资源过多,但又经常用到的对象(比如session工厂、数据源等) ## 1. 饿汉式 - 静态常量写法 ### 代码实现: ```java /** * 设计模式之单例模式 * 饿汉式(静态常量) */ public class SingletonTest01 { public static void main(String[] args) { Singleton instance1 = Singleton.getInstance(); Singleton instance2 = Singleton.getInstance(); System.out.println("两次获取的实例一样吗:" + (instance1 == instance2)); //true } } class Singleton { //私有构造方法,使其不可在外部通过构造器实例化 private Singleton() { } //定义为常量,保证实例对象不变 private final static Singleton instance = new Singleton(); //通过此方法获取实例 public static Singleton getInstance() { return instance; } } ``` ### 分析: 优点: - 使用方式简单,在类加载的时候创建实例对象,避免了线程同步问题 缺点: - 在类加载的时候创建实例对象,但不确定何时使用、是否使用,可能造成内存浪费 ## 2. 饿汉式 - 静态代码块写法 ### 代码实现: ```java /** * 设计模式之单例模式 * 饿汉式(静态代码块写法) */ class Singleton{ //私有构造方法,使其不可在外部通过构造器实例化 private Singleton(){ } //定义为常量,保证实例对象不变 private final static Singleton instance; static { instance = new Singleton(); } //通过此方法获取实例 public static Singleton getInstance(){ return instance; } } ``` ### 分析: 和静态常量一致,只不过初始化的位置不同,一个在静态代码块,一个直接在常量声明处初始化 ## 3. 懒汉式 - 线程不安全 ### 代码实现: ```java /** * 设计模式之单例模式 * 懒汉式(线程不安全) */ class Singleton { //私有构造方法,使其不可在外部通过构造器实例化 private Singleton() { } //声明实例对象 private static Singleton instance; //通过此方法获取实例 public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } ``` ### 分析: 优点: - 满足随用随拿的特点,解决了内存浪费的问题 缺点: - 线程不安全,当多个线程访问时,可能创建多个实例,因此实际开发中不可使用 ## 4. 懒汉式 - 线程安全 - 同步方法写法 ### 代码实现: ```java /** * 设计模式之单例模式 * 懒汉式(同步方法) */ class Singleton { //私有构造方法,使其不可在外部通过构造器实例化 private Singleton() { } //声明实例对象 private static Singleton instance; //通过此方法获取实例 public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } ``` ### 分析: 虽然解决了线程不安全问题,但锁的范围太大,效率低,开发中尽量不要使用 ## 5. 懒汉式 - 线程安全 - 同步代码块写法 ### 代码实现: ```java /** * 设计模式之单例模式 * 懒汉式(同步代码块写法) */ class Singleton { //私有构造方法,使其不可在外部通过构造器实例化 private Singleton() { } //声明实例对象 private static Singleton instance; //通过此方法获取实例 public static Singleton getInstance() { if (instance == null) { //同步代码 synchronized (Singleton.class) { instance = new Singleton(); } } return instance; } } ``` ### 分析: 这种方式将同步锁缩小了范围,本意是解决效率问题,但又造成了线程不安全,因此开发中不可使用 ## 6. 懒汉式 - 双重检查(推荐使用) ### 代码实现: ```java /** * 设计模式之单例模式 * 双重检查 */ 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; } } ``` ### 分析: 既提高了效率也解决了线程安全问题,推荐使用这种方法 ## 7. 懒汉式 - 静态内部类(推荐使用) ### 代码实现: ```java /** * 设计模式之单例模式 * 静态内部类 */ class Singleton { //私有构造方法,使其不可在外部通过构造器实例化 private Singleton() { } //静态内部类 private static class SingletonInstance{ private final static Singleton INSTANCE = new Singleton(); } //获取实例 public static Singleton getInstance() { return SingletonInstance.INSTANCE; } } ``` ### 分析: 利用了类加载机制,保证初始化实例时只有一个线程。Singleton类被装载时并不会被实例化,当调用getInstance方法时才会装载SingletonInstance ## 8. 懒汉式 - 枚举法(推荐使用) ### 代码实现: ```java /** * 设计模式之单例模式 * 枚举 */ enum Singleton{ INSTANCE; } ``` ### 分析: 不仅能规避线程不安全,还能防止反序列化重新创建新的对象 # 二、工厂模式 ## 1. 简单工厂模式 ### 1.1 介绍 ​ 严格来说,简单工厂模式并不是23种常见的设计模式之一,它只算工厂模式的一个特殊实现。简单工厂模式在实际中的应用相对于其他2个工厂模式用的还是相对少得多,因为它只适应很多简单的情况。 ​ 简单工厂模式违背了 **开闭原则** (但可以通过反射的机制来避免) 。因为每次你要新添加一个功能,都需要在生switch-case 语句(或者if-else 语句)中去修改代码,添加分支条件。 ### 1.2 适用场景 - 需要创建的对象较少 - 客户端不关心对象的创建过程 ### 1.3 简单工厂模式角色分配 - **工厂(Factory)角色** :简单工厂模式的核心,它负责实现创建所有实例的内部逻辑。工厂类可以被外界直接调用,创建所需的产品对象。 - **抽象产品(Product)角色** :简单工厂模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。 - **具体产品(Concrete Product)角色**:简单工厂模式的创建目标,所有创建的对象都是充当这个角色的某个具体类的实例。 ### 1.4 简单工厂模式代码实现 新建一个抽象产品 Shape ```java public interface Shape { void draw(); } ``` 具体产品 Circle、Square,实现 Shape 接口 ```java public class Circle implements Shape { @Override public void draw() { System.out.println("圆形"); } } public class Square implements Shape { @Override public void draw() { System.out.println("正方形"); } } ``` 工厂 ShapeFactory ```java public class ShapeFactory { public static Shape getShape(String name){ if(name == null){ return null; } if ("SQUARE".equalsIgnoreCase(name)){ return new Square(); }else if("CIRCLE".equalsIgnoreCase(name)){ return new Circle(); }else{ return null; } } } ``` 客户端 Client ```java public class Client { public static void main(String[] args) { Shape circle = ShapeFactory.getShape("circle"); circle.draw(); Shape square = ShapeFactory.getShape("square"); square.draw(); } } ``` 运行结果 ``` 圆形 正方形 ``` 虽然实现了简单工厂模式,但是当我们新增一个需求的时候,需要修改ShapeFactory类的代码,违反了开闭原则,我们可以用反射的方式重写工厂方法 ```java public class ShapeFactory2 { public static Object getClass(Class<? extends Shape> clazz){ if (clazz == null){ return null; } try { return clazz.newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return null; } } ``` 测试 ```java public class Client2 { public static void main(String[] args) { Circle circle = (Circle) ShapeFactory2.getClass(Circle.class); circle.draw(); Square square = (Square) ShapeFactory2.getClass(Square.class); square.draw(); } } ``` 运行结果 ```java 圆形 正方形 ``` ## 2. 工厂方法模式 ### 2.1 介绍 ​ 工厂方法模式应该是在工厂模式家族中是用的最多模式,一般项目中存在最多的就是这个模式。 ​ **工厂方法模式是简单工厂的仅一步深化**, 在工厂方法模式中,我们不再提供一个统一的工厂类来创建所有的对象,而是针对不同的对象提供不同的工厂。也就是说 **每个对象都有一个与之对应的工厂** 。 ### 2.2 适用场景 - 一个类不知道它所需要的对象的类:在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建;客户端需要知道创建具体产品的工厂类。 - 一个类通过其子类来指定创建哪个对象:在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏 - 将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无需关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。 ### 2.3 工厂方法模式角色分配 - **抽象工厂(Abstract Factory)角色**:是工厂方法模式的核心,与应用程序无关。任何在模式中创建的对象的工厂类必须实现这个接口。 - **具体工厂(Concrete Factory)角色** :这是实现抽象工厂接口的具体工厂类,包含与应用程序密切相关的逻辑,并且受到应用程序调用以创建某一种产品对象。 - **抽象产品(Abstract Product)角色** :工厂方法模式所创建的对象的超类型,也就是产品对象的共同父类或共同拥有的接口。 - **具体产品(Concrete Product)角色** :这个角色实现了抽象产品角色所定义的接口。某具体产品有专门的具体工厂创建,它们之间往往一一对应 ### 2.4 工厂方法模式代码实现 抽象工厂 Factory ```java public interface Factory { Shape getShape(); } ``` 具体工厂 CircleFactory、SquareFactory ```java public class CircleFactory implements Factory { @Override public Shape getShape() { return new Circle(); } } public class SquareFactory implements Factory { @Override public Shape getShape() { return new Square(); } } ``` 抽象产品和具体产品继续使用简单工厂模式中的类 客户端 ```java public class Client { public static void main(String[] args) { Shape circle = new CircleFactory().getShape(); circle.draw(); Shape square = new SquareFactory().getShape(); square.draw(); } } ``` 运行结果 ```java 圆形 正方形 ``` ## 3. 抽象工厂模式 ### 3.1 介绍 ​ 在工厂方法模式中,其实我们有一个潜在意识的意识。那就是我们生产的都是同一类产品。抽象工厂模式是工厂方法的仅一步深化,在这个模式中的工厂类不单单可以创建一种产品,而是可以创建一组产品。 ​ 将工厂抽象成两层,Abstract Factory(抽象工厂)和具体实现的工厂子类。程序员可以根据创建对象类型使用对应的工厂子类。这样将单个的简单工厂类变成了工厂簇,更利于代码的维护和扩展。 ### 3.2 适用场景 - 和工厂方法一样客户端不需要知道它所创建的对象的类。 - 需要一组对象共同完成某种功能时,并且可能存在多组对象完成不同功能的情况。(同属于同一个产品族的产品) - 系统结构稳定,不会频繁的增加对象。(因为一旦增加就需要修改原有代码,不符合开闭原则) ### 3.3 抽象工厂模式角色分配 - **抽象工厂(Abstract Factory)角色** :是工厂方法模式的核心,与应用程序无关。任何在模式中创建的对象的工厂类必须实现这个接口。 - **具体工厂类(Concrete Factory)角色** :这是实现抽象工厂接口的具体工厂类,包含与应用程序密切相关的逻辑,并且受到应用程序调用以创建某一种产品对象。 - **抽象产品(Abstract Product)角色** :工厂方法模式所创建的对象的超类型,也就是产品对象的共同父类或共同拥有的接口。 - **具体产品(Concrete Product)角色** :抽象工厂模式所创建的任何产品对象都是某一个具体产品类的实例。在抽象工厂中创建的产品属于同一产品族,这不同于工厂模式中的工厂只创建单一产品。 ### 3.4 抽象工厂的工厂和工厂方法中的工厂有什么区别? ​ 抽象工厂是生产一整套有产品的(至少要生产两个产品),这些产品必须相互是有关系或有依赖的,而工厂方法中的工厂是生产单一产品的工厂。 ### 3.5 抽象工厂模式代码实现 抽象产品:Gun、Bullet ```java public interface Gun { void shooting(); } public interface Bullet { void loading(); } ``` 具体产品:AK47、AK47Bullet、M16、M16Bullet ```java public class AK47 implements Gun { @Override public void shooting() { System.out.println("AK47射击"); } } public class AK47Bullet implements Bullet { @Override public void loading() { System.out.println("AK47装子弹"); } } public class M16 implements Gun { @Override public void shooting() { System.out.println("M16射击"); } } public class M16Bullet implements Bullet { @Override public void loading() { System.out.println("M16装子弹"); } } ``` 抽象工厂:ArmsFactory ```java public interface ArmsFactory { Gun produceGun(); Bullet produceBullet(); } ``` 具体工厂: ```java public class AK47Factory implements ArmsFactory{ @Override public Gun produceGun() { return new AK47(); } @Override public Bullet produceBullet() { return new AK47Bullet(); } } public class M16Factory implements ArmsFactory{ @Override public Gun produceGun() { return new M16(); } @Override public Bullet produceBullet() { return new M16Bullet(); } } ``` 测试 ```java public class Client { public static void main(String[] args) { ArmsFactory factory; factory = new AK47Factory(); Gun ak47 = factory.produceGun(); Bullet ak47Bullet = factory.produceBullet(); ak47Bullet.loading(); ak47.shooting(); factory = new M16Factory(); Gun m16 = factory.produceGun(); Bullet m16Bullet = factory.produceBullet(); m16Bullet.loading(); m16.shooting(); } } ``` 结果 ``` AK47装子弹 AK47射击 M16装子弹 M16射击 ``` 参考:[深入理解工厂模式](https://zhuanlan.zhihu.com/p/37362917) # 三、原型模式 原型模式是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象。 原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节 可以通过重写clone方法实现拷贝,拷贝又分为浅拷贝和深拷贝 ## 1. 浅拷贝: - 对于基本数据类型的成员变量,浅拷贝会直接进行值传递,将该属性值复制一份给新的对象 - 对于引用类型的成员变量,浅拷贝知识将该成员变量的引用值(内存地址)复制一份给新的对象,因此会造成一个对象修改值影响另外一个对象 - 浅拷贝时使用默认的clone()方法来实现,例如: `Person = (Person)super.clone()` ## 2. 深拷贝: - 复制对象的所有基本数据类型的成员变量值 - 为所有引用类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象 - 可通过重写clone方法和对象序列化方式(推荐)实现 ```java //使用clone方法 @Override protected Object clone() throws CloneNotSupportedException { Company company = null; try { company = (Company) super.clone(); //处理引用类型 company.employee = (Employee) employee.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return company; } ``` ```java //使用序列化 protected Object deepClone(){ ByteArrayInputStream bis = null; ByteArrayOutputStream bos = null; ObjectInputStream ois = null; ObjectOutputStream oos = null; try { //序列化 bos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(bos); oos.writeObject(this); //反序列化 bis = new ByteArrayInputStream(bos.toByteArray()); ois = new ObjectInputStream(bis); return ois.readObject(); }catch (Exception e){ e.printStackTrace(); } return null; } ``` ## 3. 原型模式的分析: - 创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率 - 不用重新初始化对象,而是动态的获取对象运行时的状态 - 如果原始对象发生变化(增加或减少属性),其它克隆对象也会发生变化,无需修改代码 - 浅拷贝和深拷贝对引用类型的处理方式不一样 # 四、建造者模式 ## 1. 基本介绍 1. 建造者模式(Builder Pattern)又叫生成器模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。 2. 建造者模式是一步步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构造它们,用户不需要知道内部的具体构建细节。 ## 2. 建造者模式的四个角色 - **产品角色(Product)**:一个具体的产品对象 - **抽象建造者(Builder)**:创建一个Product对象的各个部件指定的**接口**或**抽象类** - **具体建造者(Concrete Builder)**:实现接口,构建和装配各个部件。 - **指挥者(Director)**:构建一个使用Builder接口的对象。它主要是用于创建一个复杂的对象。它主要有两个作用:① 隔离了客户与对象的生产过程,②负责控制产品对象的生产过程。 ## 3. 原理类图: ![](https://gitee.com/songjilong/FigureBed/raw/master/img/20200319164256.png) ## 4. 建造者模式在 JDK - StringBuilder 中的应用 **在 StringBuilder 继承关系中:** StringBuilder 继承了 AbstractStringBuilder ,AbstractStringBuilder 实现了 Appendable 接口 **角色分析:** - 抽象建造者:Appendable - 具体建造者:AbstractStringBuilder - 指挥者:StringBuilder ## 5. 建造者模式注意事项与细节 1. 客户端(使用程序)**不必知道产品内部组成的细节**,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象 2. **每一个具体建造者都相对独立,而与其他的具体建造者无关**,因此可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象 3. **可以更加精细地控制产品的创建过程**。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程 4. **增加新的具体建造者无须修改原有类库的代码**,指挥者类针对抽象建造者类编程,系统扩展方便,符合“开闭原则” ## 6. 建造者模式代码实现 具体产品:House ```java public class House { private String basic; private String wall; private String roofed; //省略getter setter toString()方法 } ``` 抽象建造者:HouseBuilder ```java public abstract class HouseBuilder { protected House house = new House(); public abstract void buildBasic(); public abstract void buildWall(); public abstract void buildRoof(); public House buildHouse() { return house; } } ``` 具体建造者:CommonHouse、HighHouse ```java public class CommonHouse extends HouseBuilder { @Override public void buildBasic() { System.out.println("打10m的地基"); } @Override public void buildWall() { System.out.println("砌20cm的墙"); } @Override public void buildRoof() { System.out.println("封瓦片顶"); } } public class HighHouse extends HouseBuilder { @Override public void buildBasic() { System.out.println("打20m的地基"); } @Override public void buildWall() { System.out.println("砌50cm的墙"); } @Override public void buildRoof() { System.out.println("封玻璃顶"); } } ``` 指挥者:HouseDirector ```java public class HouseDirector { private HouseBuilder builder; public HouseDirector(HouseBuilder builder) { this.builder = builder; } public House build(){ builder.buildBasic(); builder.buildWall(); builder.buildRoof(); return builder.buildHouse(); } } ``` 测试: ```java public class Client { public static void main(String[] args) { HouseDirector builderDirector1 = new HouseDirector(new CommonHouse()); builderDirector1.build(); System.out.println("---------"); HouseDirector builderDirector2 = new HouseDirector(new HighHouse()); builderDirector2.build(); } } ``` 结果: ``` 打10m的地基 砌20cm的墙 封瓦片顶 --------- 打20m的地基 砌50cm的墙 封玻璃顶 ```

加载全部内容

相关教程
猜你喜欢
用户评论