面向对象编程-策略模式-建立型

 

      晴川历历汉阳树,芳草萋萋鹦鹉洲。

 

介绍:面向对象编程-策略模式-建立型。

一、简述

什么叫策略模式

策略模式(Design Pattern)是一套被不断应用、大部分人了解的、历经归类的、代码设计工作经验的汇总。

策略模式的益处&学习培训目地

1、为了更好地编码可器重行、让编码更加容易被别人了解、确保编码的稳定性、使编码撰写真真正正完成产品化;

2、策略模式有利于大家维护保养新项目,提高系统软件的可扩展性和扩展性;

3、策略模式还能够锻练程序员的设计创意、提升编码品质等。

二、建立型

单例模式、简易工厂模式、工厂方法方式、抽象性工厂模式、制作器方式、原型模式。

1、单例模式(Singleton)

单例模式参照连接:https://www.cnblogs.com/taojietaoge/p/10336439.html

Intent

保证一个类只有一个案例,并给予该案例的全局性浏览点。

Class Diagram

应用一个独享构造方法、一个独享静态变量及其一个公有制静态函数来完成。

独享构造方法确保了不可以根据构造方法来创建对象案例,只有根据公有制静态函数回到唯一的独享静态变量。

 

Implementation

Ⅰ 饱汉式-进程不安全

下列完成中,独享静态变量 uniqueInstance 被延迟时间创建对象,那样做的益处是,要是没有采用此类,那麼就不容易创建对象 uniqueInstance,进而节约能源。

这一完成在线程同步自然环境下是不安全的,假如好几个进程可以与此同时进到 if (uniqueInstance == null) ,而且这时 uniqueInstance 为 null,那麼会出现好几个进程实行 uniqueInstance = new Singleton(); 句子,这将造成创建对象数次 uniqueInstance。

 1 // 饱汉方式-进程不安全
 2 public class Singleton {
 3 
 4     private static Singleton uniqueInstance;
 5 
 6     private Singleton() {
 7     }
 8 
 9     public static Singleton getUniqueInstance() {
10         if (uniqueInstance == null) {
11             uniqueInstance = new Singleton();
12         }
13         return uniqueInstance;
14     }
15 }
View Code

Ⅱ 饿汉式-线程安全

进程不安全隐患主要是因为 uniqueInstance 被创建对象数次,采用立即创建对象 uniqueInstance 的方法就不容易造成进程不安全隐患。

可是立即创建对象的方法也遗失了延迟时间创建对象产生的节约能源的益处。

 1 // 饿汉方式
 2 public class Singleton {
 3 
 4     private static Singleton uniqueInstance = new Singleton();
 5 
 6     private Singleton() {
 7     }
 8 
 9     public static Singleton getUniqueInstance() {
10         return uniqueInstance;
11     }
12 }
View Code

Ⅲ 饱汉式-线程安全

只必须 对 getUniqueInstance() 方式上锁,那麼在一个时间点只有有一个进程可以进到该方式,进而防止了创建对象数次 uniqueInstance。

可是当一个进程进到该方式以后,其他尝试进到该方式的进程都务必等候,即便 uniqueInstance 早已被创建对象了。这会让进程堵塞時间太长,因而该方式有特性难题,不强烈推荐应用。

 1 // 饱汉方式-线程安全方式
 2 public class Singleton {
 3 
 4     private static Singleton uniqueInstance;
 5 
 6     private Singleton() {
 7     }
 8 
 9     public static synchronized Singleton getUniqueInstance() {  // synchronized 上锁
10         if (uniqueInstance == null) {
11             uniqueInstance = new Singleton();
12         }
13         return uniqueInstance;
14     }
15 }
View Code

Ⅳ 双向校检锁-线程安全

uniqueInstance 只必须 被创建对象一次,以后就可以立即应用了。上锁实际操作只必须 对创建对象那一部分的编码开展,仅有当 uniqueInstance 沒有被创建对象时,才必须 开展上锁。

双向校检锁先分辨 uniqueInstance 是不是早已被创建对象,要是没有被创建对象,那麼才对创建对象句子开展上锁。

 1 // 双向校检锁
 2 public class Singleton {
 3 
 4     private volatile static Singleton uniqueInstance;  // 应用 volatile 关键词装饰,严禁JVM 命令重排列
 5 
 6     private Singleton() {
 7     }
 8 
 9     public static Singleton getUniqueInstance() {
10         if (uniqueInstance == null) {
11             synchronized (Singleton.class) {  // synchronized 对创建对象句子上锁
12                 if (uniqueInstance == null) {
13                     uniqueInstance = new Singleton();
14                 }
15             }
16         }
17         return uniqueInstance;
18     }
19 }
View Code

考虑到下边的完成,也就是只应用了一个 if 句子。在 uniqueInstance == null 的状况下,假如2个进程都实行了 if 句子,那麼2个进程都是会进到 if 句子块内。尽管在 if 句子块内有上锁实际操作,可是2个进程都是会实行 uniqueInstance = new Singleton(); 这条句子,仅仅依次的难题,那麼便会开展2次创建对象。因而务必应用双向校检锁,也就是必须 应用2个 if 句子:第一个 if 句子用于防止 uniqueInstance 早已被创建对象以后的上锁实际操作,而第二个 if 句子开展了上锁,因此只有有一个进程进到,就不容易发生 uniqueInstance == null 时2个进程与此同时开展创建对象实际操作。

1 if (uniqueInstance == null) {
2     synchronized (Singleton.class) {  // 假如2个进程都实行了 if 句子
3         uniqueInstance = new Singleton();
4     }
5 }

uniqueInstance 选用 volatile 关键词装饰也是很必须的, uniqueInstance = new Singleton(); 这一段编码实际上 是分成三步实行:

  1. 为 uniqueInstance 释放内存室内空间
  2. 复位 uniqueInstance
  3. 将 uniqueInstance 偏向分派的基址

可是因为 JVM 具备命令重新排列的特点,实行次序有可能变为 1>3>2。命令重新排列在单核自然环境下不容易发生难题,可是在线程同步自然环境下能造成一个进程得到 都还没复位的案例。比如,进程 T1 实行了 1 和 3,这时 T2 启用 getUniqueInstance() 后发觉 uniqueInstance 不以空,因而回到 uniqueInstance,但这时 uniqueInstance 还未被复位。

应用 volatile 能够 严禁 JVM 的命令重新排列,确保在线程同步自然环境下也可以一切正常运作。

volatile 怎样确保严禁命令重排列参照连接:https://www.cnblogs.com/taojietaoge/p/10260888.html

Ⅴ 静态内部类完成

当 Singleton 类被载入时,静态内部类 SingletonHolder 沒有被载入进运行内存。仅有当启用 getUniqueInstance() 方式进而开启 SingletonHolder.INSTANCE 时 SingletonHolder 才会被载入,这时复位 INSTANCE 案例,而且 JVM 能保证 INSTANCE 只被创建对象一次。

这类方法不但具备延迟时间复位的益处,并且由 JVM 给予了对线程安全的适用。

Ⅵ 枚举类型完成

1 应用枚举类完成单例模式的益处:书写简易、默认设置线程安全、反序列化(反射面、复制)时也不会建立新的目标。

 1 // 枚举类完成单例模式
 2 public enum Singleton {
 3 
 4     INSTANCE;
 5 
 6     private String objName;
 7 
 8 
 9     public String getObjName() {
10         return objName;
11     }
12 
13 
14     public void setObjName(String objName) {
15         this.objName = objName;
16     }
17 
18 
19     public static void main(String[] args) {
20 
21         // 单例模式检测
22         Singleton firstSingleton = Singleton.INSTANCE;
23         firstSingleton.setObjName("firstName");
24         System.out.println(firstSingleton.getObjName());
25         Singleton secondSingleton = Singleton.INSTANCE;
26         secondSingleton.setObjName("secondName");
27         System.out.println(firstSingleton.getObjName());
28         System.out.println(secondSingleton.getObjName());
29 
30         // 反射面获得案例检测
31         try {
32             Singleton[] enumConstants = Singleton.class.getEnumConstants();
33             for (Singleton enumConstant : enumConstants) {
34                 System.out.println(enumConstant.getObjName());
35             }
36         } catch (Exception e) {
37             e.printStackTrace();
38         }
39     }
40 }
View Code
1 輸出:
2 firstName
3 secondName
4 secondName
5 secondName

该完成能够 避免反射面进攻。在其他完成中,根据 setAccessible() 方式能够 将独享构造方法的浏览等级设定为 public,随后启用构造方法进而创建对象目标,假如要避免这类进攻,必须 在构造方法中加上避免数次创建对象的编码。该完成是由 JVM 确保只能创建对象一次,因而不容易发生以上的反射面进攻。

该完成在数次实例化和实例化以后,不容易获得好几个案例。而其他完成必须 应用 transient 装饰全部字段名,而且完成实例化和反序列化的方式。

单例模式总结

最好的单例模式完成方式便是枚举类型方式。运用枚举类型的特点,让JVM 来帮大家确保线程安全和单一案例。

2、简易加工厂(Simple Factory)

Intent

在建立一个目标时不向顾客曝露內部关键点,并给予一个创建对象的通用性插口。

Class Diagram

简易加工厂把创建对象的实际操作独立放进一个类中,这一类就变成简易加工厂类,让简易加工厂类来决策应当用哪一个实际派生类来创建对象。

那样做可以把顾客类和实际派生类的完成解耦,顾客类不会再必须 了解有什么派生类及其理应创建对象哪一个派生类。顾客类通常有好几个,如果不应用简易加工厂,那麼全部的顾客类都需要了解全部派生类的关键点。并且一旦派生类发生改变,比如提升派生类,那麼全部的顾客类都需要开展改动。

Implementation

1 public interface Product {
2 }
1 public class ConcreteProduct implements Product {
2 }
1 public class ConcreteProduct1 implements Product {
2 }
public class ConcreteProduct2 implements Product {
}

下列的 Client 类包括了创建对象的编码,它是一种不正确的完成。假如在顾客类中存有这类创建对象编码,就必须 考虑到将编码放进简易加工厂中。

 1 // 不正确完成
 2 public class Client {
 3 
 4     public static void main(String[] args) {
 5         int type = 1;
 6         Product product;
 7         if (type == 1) {
 8             product = new ConcreteProduct1();
 9         } else if (type == 2) {
10             product = new ConcreteProduct2();
11         } else {
12             product = new ConcreteProduct();
13         }
14         // do what u want to do
15     }
16 }
View Code

下列的 SimpleFactory 是简易加工厂完成,它被全部必须 开展创建对象的顾客类启用。

 1 public class SimpleFactory {
 2 
 3     public Product createProduct(int type) {
 4         if (type == 1) {  // 还可以用switch 当目标种类过多
 5             return new ConcreteProduct1();
 6         } else if (type == 2) {
 7             return new ConcreteProduct2();
 8         }
 9         return new ConcreteProduct();
10     }
11 }
1 public class Client {
2     public static void main(String[] args) {
3         SimpleFactory simpleFactory = new SimpleFactory();
4         // 只必须 传一个种类主要参数,创建对象实际目标  
5         Product product1 = simpleFactory.createProduct(1);
6         Product product2 = simpleFactory.createProduct(2);
7     }
8 }

3、工厂方法(Factory Method)

Intent

界定了一个创建对象的插口,但由派生类决策要创建对象哪一个类。工厂方法把创建对象实际操作延迟到派生类。

Class Diagram

在简易加工厂中,创建对象的是另一个类,而在工厂方法中,是由派生类来创建对象。

下面的图中,Factory 有一个 doSomething() 方式,这一方式必须 采用一个商品目标,这一商品目标由 factoryMethod() 方式建立。该方式是抽象性的,必须 由派生类去完成。

 

Implementation

1 public abstract class Factory {
2     abstract public Product factoryMethod();  // 抽象方法,由派生类去完成
3     public void doSomething() {
4         Product product = factoryMethod();
5         // do something with the product
6     }
7 }
1 public class ConcreteFactory extends Factory {
2     public Product factoryMethod() {
3         return new ConcreteProduct();
4     }
5 }
1 public class ConcreteFactory1 extends Factory {
2     public Product factoryMethod() {
3         return new ConcreteProduct1();
4     }
5 }
1 public class ConcreteFactory2 extends Factory {
2     public Product factoryMethod() {
3         return new ConcreteProduct2();  // 由派生类来创建对象
4     }
5 }

4、抽象性加工厂(Abstract Factory)

Intent

给予一个插口,用以建立 有关的目标大家族 。

Class Diagram

抽象性工厂模式建立的是目标大家族,也就是许多目标而不是一个目标,而且这种目标是有关的,换句话说务必一起建立出去。而工厂方法方式仅仅用以建立一个目标,这和抽象性工厂模式有非常大不一样。

抽象性工厂模式采用了工厂方法方式来建立单一目标,AbstractFactory 中的 createProductA() 和 createProductB() 方式全是让派生类来完成,这两个方式独立看来便是在建立一个目标,这合乎工厂方法方式的界定。

对于创建对象的大家族这一定义是在 Client 反映,Client 要根据 AbstractFactory 与此同时启用2个方式来建立出2个目标,在这儿这两个目标就会有非常大的关联性,Client 必须 与此同时建立出这两个目标。

从高端看来,抽象性加工厂应用了组成,即 Cilent 组成了 AbstractFactory,而工厂方法方式应用了承继。

 

Implementation

1 public class AbstractProductA {
2 }
1 public class AbstractProductB {
2 }
1 public class ProductA1 extends AbstractProductA {
2 }
1 public class ProductA2 extends AbstractProductA {
2 }
1 public class ProductB1 extends AbstractProductB {
2 }
1 public class ProductB2 extends AbstractProductB {
2 }
1 public abstract class AbstractFactory {
2     abstract AbstractProductA createProductA();
3     abstract AbstractProductB createProductB();
4 }
1 public class ConcreteFactory1 extends AbstractFactory {
2     AbstractProductA createProductA() {
3         return new ProductA1();
4     }
5 
6     AbstractProductB createProductB() {
7         return new ProductB1();
8     }
9 }
1 public class ConcreteFactory2 extends AbstractFactory {
2     AbstractProductA createProductA() {
3         return new ProductA2();
4     }
5 
6     AbstractProductB createProductB() {
7         return new ProductB2();
8     }
9 }
1 public class Client {
2     public static void main(String[] args) {
3         AbstractFactory abstractFactory = new ConcreteFactory1();
4         AbstractProductA productA = abstractFactory.createProductA();
5         AbstractProductB productB = abstractFactory.createProductB();
6         // do something with productA and productB
7     }
8 }

6、制作器(Builder)

Intent

封裝一个目标的结构全过程,并容许按流程结构。

Class Diagram

 

Implementation

下列是一个简单的 StringBuilder 完成,参照了 JDK 1.8 源代码。

 1 public class AbstractStringBuilder {
 2     protected char[] value;
 3 
 4     protected int count;
 5 
 6     public AbstractStringBuilder(int capacity) {
 7         count = 0;
 8         value = new char[capacity];
 9     }
10 
11     public AbstractStringBuilder append(char c) {
12         ensureCapacityInternal(count   1);
13         value[count  ] = c;
14         return this;
15     }
16 
17     private void ensureCapacityInternal(int minimumCapacity) {
18         // overflow-conscious code
19         if (minimumCapacity - value.length > 0)
20             expandCapacity(minimumCapacity);
21     }
22 
23     void expandCapacity(int minimumCapacity) {
24         int newCapacity = value.length * 2   2;
25         if (newCapacity - minimumCapacity < 0)
26             newCapacity = minimumCapacity;
27         if (newCapacity < 0) {
28             if (minimumCapacity < 0) // overflow
29                 throw new OutOfMemoryError();
30             newCapacity = Integer.MAX_VALUE;
31         }
32         value = Arrays.copyOf(value, newCapacity);
33     }
34 }
View Code
 1 public class StringBuilder extends AbstractStringBuilder {
 2     public StringBuilder() {
 3         super(16);
 4     }
 5 
 6     @Override
 7     public String toString() {
 8         // Create a copy, don't share the array
 9         return new String(value, 0, count);
10     }
11 }
View Code
 1 public class Client {
 2     public static void main(String[] args) {
 3         StringBuilder sb = new StringBuilder();
 4         final int count = 26;
 5         for (int i = 0; i < count; i  ) {
 6             sb.append((char) ('a'   i));
 7         }
 8         System.out.println(sb.toString());
 9     }
10 }
View Code
abcdefghijklmnopqrstuvwxyz

6、原型模式(Prototype)

Intent

应用原形案例特定要创建对象的种类,根据拷贝这一原形来建立新目标。

Class Diagram

Implementation

1 public abstract class Prototype {
2     abstract Prototype myClone();
3 }
 1 public class ConcretePrototype extends Prototype {
 2 
 3     private String filed;
 4 
 5     public ConcretePrototype(String filed) {
 6         this.filed = filed;
 7     }
 8 
 9     @Override
10     Prototype myClone() {
11         return new ConcretePrototype(filed);
12     }
13 
14     @Override
15     public String toString() {
16         return filed;
17     }
18 }
View Code
1 public class Client {
2     public static void main(String[] args) {
3         Prototype prototype = new ConcretePrototype("abc");
4         Prototype clone = prototype.myClone();
5         System.out.println(clone.toString());  // abc
6     }
7 }

 

 

 

 

晴川历历汉阳树

      芳草萋萋鹦鹉洲

 

 

 

评论(0条)

刀客源码 游客评论