模式 & 描述 | 包括 |
---|---|
创建型模式 这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。 | 工厂模式(Factory Pattern) 抽象工厂模式(Abstract Factory Pattern) 单例模式(Singleton Pattern) 建造者模式(Builder Pattern) 原型模式(Prototype Pattern) |
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
我们将创建一个 Shape
接口和实现 Shape
接口的实体类。下一步是定义工厂类 ShapeFactory
。
FactoryPatternDemo
类使用 ShapeFactory
来获取 Shape
对象。它将向 ShapeFactory
传递信息(CIRCLE
/ RECTANGLE
/ SQUARE
),以便获取它所需对象的类型。
Shape.java
public interface Shape {
void draw();
}
Rectangle.java
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
Square.java
public class Square implements Shape {
@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
Circle.java
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
ShapeFactory.java
public class ShapeFactory {
//使用 getShape 方法获取形状类型的对象
public Shape getShape(String shapeType){
if(shapeType == null){
return null;
}
if(shapeType.equalsIgnoreCase("CIRCLE")){
return new Circle();
} else if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();
} else if(shapeType.equalsIgnoreCase("SQUARE")){
return new Square();
}
return null;
}
}
FactoryPatternDemo.java
public class FactoryPatternDemo {
public static void main(String[] args) {
ShapeFactory shapeFactory = new ShapeFactory();
//获取 Circle 的对象,并调用它的 draw 方法
Shape shape1 = shapeFactory.getShape("CIRCLE");
//调用 Circle 的 draw 方法
shape1.draw();
//获取 Rectangle 的对象,并调用它的 draw 方法
Shape shape2 = shapeFactory.getShape("RECTANGLE");
//调用 Rectangle 的 draw 方法
shape2.draw();
//获取 Square 的对象,并调用它的 draw 方法
Shape shape3 = shapeFactory.getShape("SQUARE");
//调用 Square 的 draw 方法
shape3.draw();
}
}
Inside Circle::draw() method.
Inside Rectangle::draw() method.
Inside Square::draw() method.
抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。
意图:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
主要解决:主要解决接口选择的问题。
何时使用:系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。
如何解决:在一个产品族里面,定义多个产品。
关键代码:在一个工厂里聚合多个同类产品。
应用实例:
优点:当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
缺点:产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码。
使用场景:
注意事项:产品族难扩展,产品等级易扩展。
我们将创建 Shape
和 Color
接口和实现这些接口的实体类。下一步是创建抽象工厂类 AbstractFactory
。接着定义工厂类 ShapeFactory
和 ColorFactory
,这两个工厂类都是扩展了 AbstractFactory
。然后创建一个工厂创造器/生成器类 FactoryProducer
。
AbstractFactoryPatternDemo
类使用 FactoryProducer
来获取 AbstractFactory
对象。它将向 AbstractFactory
传递形状信息 Shape
(CIRCLE
/ RECTANGLE
/ SQUARE
),以便获取它所需对象的类型。同时它还向 AbstractFactory
传递颜色信息 Color
(RED
/ GREEN
/ BLUE
),以便获取它所需对象的类型。
Shape.java
public interface Shape {
void draw();
}
Rectangle.java
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
Square.java
public class Square implements Shape {
@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
Circle.java
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
Color.java
public interface Color {
void fill();
}
Red.java
public class Red implements Color {
@Override
public void fill() {
System.out.println("Inside Red::fill() method.");
}
}
Green.java
public class Green implements Color {
@Override
public void fill() {
System.out.println("Inside Green::fill() method.");
}
}
Blue.java
public class Blue implements Color {
@Override
public void fill() {
System.out.println("Inside Blue::fill() method.");
}
}
AbstractFactory.java
public abstract class AbstractFactory {
public abstract Color getColor(String color);
public abstract Shape getShape(String shape);
}
ShapeFactory.java
public class ShapeFactory extends AbstractFactory {
@Override
public Shape getShape(String shapeType){
if(shapeType == null){
return null;
}
if(shapeType.equalsIgnoreCase("CIRCLE")){
return new Circle();
} else if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();
} else if(shapeType.equalsIgnoreCase("SQUARE")){
return new Square();
}
return null;
}
@Override
public Color getColor(String color) {
return null;
}
}
ColorFactory.java
public class ColorFactory extends AbstractFactory {
@Override
public Shape getShape(String shapeType){
return null;
}
@Override
public Color getColor(String color) {
if(color == null){
return null;
}
if(color.equalsIgnoreCase("RED")){
return new Red();
} else if(color.equalsIgnoreCase("GREEN")){
return new Green();
} else if(color.equalsIgnoreCase("BLUE")){
return new Blue();
}
return null;
}
}
FactoryProducer.java
public class FactoryProducer {
public static AbstractFactory getFactory(String choice){
if(choice.equalsIgnoreCase("SHAPE")){
return new ShapeFactory();
} else if(choice.equalsIgnoreCase("COLOR")){
return new ColorFactory();
}
return null;
}
}
public class AbstractFactoryPatternDemo {
public static void main(String[] args) {
//获取形状工厂
AbstractFactory shapeFactory = FactoryProducer.getFactory("SHAPE");
//获取形状为 Circle 的对象
Shape shape1 = shapeFactory.getShape("CIRCLE");
//调用 Circle 的 draw 方法
shape1.draw();
//获取形状为 Rectangle 的对象
Shape shape2 = shapeFactory.getShape("RECTANGLE");
//调用 Rectangle 的 draw 方法
shape2.draw();
//获取形状为 Square 的对象
Shape shape3 = shapeFactory.getShape("SQUARE");
//调用 Square 的 draw 方法
shape3.draw();
//获取颜色工厂
AbstractFactory colorFactory = FactoryProducer.getFactory("COLOR");
//获取颜色为 Red 的对象
Color color1 = colorFactory.getColor("RED");
//调用 Red 的 fill 方法
color1.fill();
//获取颜色为 Green 的对象
Color color2 = colorFactory.getColor("GREEN");
//调用 Green 的 fill 方法
color2.fill();
//获取颜色为 Blue 的对象
Color color3 = colorFactory.getColor("BLUE");
//调用 Blue 的 fill 方法
color3.fill();
}
}
Inside Circle::draw() method.
Inside Rectangle::draw() method.
Inside Square::draw() method.
Inside Red::fill() method.
Inside Green::fill() method.
Inside Blue::fill() method.
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
注意:
getInstance()
方法中需要使用同步锁 synchronized (Singleton.class)
防止多线程同时进入造成 instance
被多次实例化。我们将创建一个 SingleObject
类。SingleObject
类有它的私有构造函数和本身的一个静态实例。
SingleObject
类提供了一个静态方法,供外界获取它的静态实例。SingletonPatternDemo
类使用 SingleObject
类来获取 SingleObject
对象。
SingleObject.java
public class SingleObject {
//创建 SingleObject 的一个对象
private static SingleObject instance = new SingleObject();
//让构造函数为 private,这样该类就不会被实例化
private SingleObject(){}
//获取唯一可用的对象
public static SingleObject getInstance(){
return instance;
}
public void showMessage(){
System.out.println("Hello World!");
}
}
SingletonPatternDemo.java
public class SingletonPatternDemo {
public static void main(String[] args) {
//不合法的构造函数
//编译时错误:构造函数 SingleObject() 是不可见的
//SingleObject object = new SingleObject();
//获取唯一可用的对象
SingleObject object = SingleObject.getInstance();
//显示消息
object.showMessage();
}
}
Hello World!
单例模式的实现有多种方式,如下所示:
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
接下来介绍的几种实现方式都支持多线程,但是在性能上有所差异。
getInstance()
的性能对应用程序不是很关键(该方法使用不太频繁)。
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
它基于 classloader 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance 方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的效果。
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
getInstance()
的性能对应用程序很关键。public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
volatile关键字的主要作用是使变量在多个线程间可见,方式是强制性从公共堆栈中进行取值。
先看个例子:
RunThread.java
public class RunThread extends Thread{
private boolean isRunning = true;
public boolean isRunning() {
return isRunning;
}
public void setRunning(boolean running) {
isRunning = running;
}
@Override
public void run() {
System.out.println("进入 run 了");
while (isRunning == true){
}
System.out.println("线程被停止了");
}
}
TestMain.java
public class TestMain {
public static void main(String[] args) {
try {
RunThread thread = new RunThread();
thread.start();
Thread.sleep(1000);
thread.setRunning(false);
System.out.println("已经赋值为false");
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
运行结果如下:
程序会一直运行下去,造成死循环。因为在启动RunThread.java
线程时,变量 private boolean isRunning = true;
存在于公共堆栈及线程的私有堆栈中。线程一直在私有堆栈中取得 isRunning
的值是true。而代码 thread.setRunning(false)
虽然被执行,更新的确实公共堆栈中的 isRunning
变量值 false
,所以一直就是死循环状态。内存结构如下图所示。
线程的私有堆栈
这个问题是私有堆栈中的值和公共堆栈中的值不同不造成的。解决这样的问题就要使用 volatile
关键字了,它主要的作用就是当线程访问 isRunning
这个变量时,强制性从公共堆栈中进行取值。
更改后 RunThread.java
代码如下:
public class RunThread extends Thread{
volatile private boolean isRunning = true;
public boolean isRunning() {
return isRunning;
}
public void setRunning(boolean running) {
isRunning = running;
}
@Override
public void run() {
System.out.println("进入 run 了");
while (isRunning == true){
}
System.out.println("线程被停止了");
}
}
TestMain.java
public class TestMain {
public static void main(String[] args) {
try {
RunThread thread = new RunThread();
thread.start();
Thread.sleep(1000);
thread.setRunning(false);
System.out.println("已经赋值为false");
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
运行结果如下:
通过使用 volatile
关键字,强制的从公共内存中读取变量的值,内存结构如下图所示。
示例如下:
MyThread.java
public class MyThread extends Thread{
volatile public static int count;
private static void addCount(){
for (int i =0; i < 1000; i++){
count++;
}
System.out.println("count = " + count);
}
@Override
public void run() {
addCount();
}
}
TestRun.java
public class TestRun {
public static void main(String[] args) {
MyThread[] arr = new MyThread[100];
for (int i = 0; i < 100; i++){
arr[i] = new MyThread();
}
for (int j = 0; j < 100; j++){
arr[j].start();
}
}
}
运行结果如下:
更改 MyThread.java
文件代码如下:
public class MyThread extends Thread{
volatile public static int count;
//注意一定要添加static关键字
//这样synchronized与static锁的内容就是MyThread.class 类了,也就达到同步的效果了。
synchronized private static void addCount(){
for (int i =0; i < 1000; i++){
count++;
}
System.out.println("count = " + count);
}
@Override
public void run() {
addCount();
}
}
TestRun.java
public class TestRun {
public static void main(String[] args) {
MyThread[] arr = new MyThread[100];
for (int i = 0; i < 100; i++){
arr[i] = new MyThread();
}
for (int j = 0; j < 100; j++){
arr[j].start();
}
}
}
运行结果如下:
在本示例中,如果在方法 private static void addCount()
前加入 synchronized
同步关键字,也就没有必要再使用 volatile
关键字来声明 count
变量了。
关键字 volatile
主要使用的场合是在多个线程中可以感知实例变量被更改了,并且可以获得最新的 值使用,也就是用多线程读取共享变量时可以获得最新值使用。
关键字 volatile
提示线程每次从共享内存中读取变量,而不是从私有内存中读取,这样就保证了同步数据的可见性。但在这里需要注意的是:如果修改实例变量中的数据,比如 i++
,也就是 i= i+1
,则这样的操作其实并不是一个原子操作,也就是非线程安全的。表达式 i++
的操作步骤分解如下:
i
的值写到内存中假如在第二步计算值的时候,另外一个线程也修改i的值,那么这个时候就会出现脏数据。解决的办法其实就是使用 synchronized
关键字。所以说 volatile
本身并不处理数据的原子性,而是强制对数据的读写及时影响到主内存的。
用图演示关键字 volatile
出现非线程安全的原因,变量在内存中工作的过程如下图所示。
可以得出以下结论:
read
和 load
阶段:从主存复制变量到当前线程工作内存;use
和 assign
阶段:执行代码,改变共享变量值。store
和 write
阶段:用工作内存数据刷新主存对应变量的值。在多线程环境中,use
和 assign
是多次出现的,但这一操作并不是原子性,也就是在 read
和 load
之后,如果主内存 count
变量发生修改之后,线程工作内存中的值由于已经加载,不会产生对应的变化 ,也就是私有内存和公共内存中的变量不同步,所以计算出来的结果会和预期不一样,也就出现了非线程安全问题。
对于用 volatile
修饰的变量,jvm
虚拟机只是保证从主内存加载到线程工作内存的值是最新的。例如 线程1
和 线程2
在进行 read
和 load
的操作中,发现主内存中 count
的值都是 5
,那么都会加载这个最新的值。也就是说,volatile
关键字解决的是变量读时的可见性问题,但无法保证原子性,对于多个线程访问同一个实例变量还是需要加锁同步。
原子类
在java.util.concurrent.atomic
包下,有一系列“Atomic”开头的类,统称为原子类。
肯定你有一个问题 为什么Atomic类可以保证原子性?
AtomicInteger
为例。在AtomicInteger
中有一个 volatile
修饰的value
变量,也就是这个整型的值。在调用getAndIncrement()
时,AtomicInteger
会通过Unsafe
类的getAndAddInt
方法对变量value
进行一次 CAS操作
。由于 CAS
是具有原子性的,所以 AtomicInteger
就保证了操作的线程安全。CAS
(compare and swap)的缩写,译为比较并交换,实现并发算法时常用到的一种技术。它包含三个操作数——内存位置、预期原值及更新值。执行 CAS
操作的时候,将内存位置的值与预期原值比较:如果相匹配,那么处理器会自动将该位置值更新为新值;如果不匹配,处理器不做任何操作,多个线程同时执行 CAS
操作只有一个会成功。
硬件级别保证
CAS
是 JDK
提供的非阻塞原子性操作,它通过硬件保证了比较——更新的原子性。它是非阻塞的且自身原子性,也就是说它效率更高且通过硬件保证,说明其更可靠。CAS
是一条 CPU
的原子指令( cmpxchg
指令),不会造成所谓的数据不一致问题,Unsafe
提供的 CAS
方法(如 compareAndSwapXXX
)底层实现即为CPU指令 cmpxchg
。执行 cmpxchg
指令的时候,会判断当前系统是否为多核系统,如果是就给总线加锁,只有一个线程会对总线加锁成功,加锁成功之后会执行 cas
操作,也就是说 CAS
的原子性实际上是 CPU
实现的, 其实在这一点上还是有排他锁的,只是比起用 synchronized
, 这里的排他时间要短的多, 所以在多线程情况下性能会比较好。代码示例
public class CASDemo {
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(5);
System.out.println(atomicInteger.get());
System.out.println(atomicInteger.compareAndSet(5,2020)+"\t"+atomicInteger.get());//执行后,expect为2020
System.out.println(atomicInteger.compareAndSet(5,1024)+"\t"+atomicInteger.get());
}
}
CAS底层原理分析(以AtomicInteger为例)
private static final Unsafe U = Unsafe.getUnsafe();
Unsafe
,CAS
的核心类,由于Java方法无法直接访问底层系统,需要通过本地(native)方法来访问,Unsafe
相当于一个后门,基于该类可以直接操作特定内存的数据。Unsafe
类存在于 sun.misc
包中,其内部方法操作可以像 C
的指针一样直接操作内存,因为 Java 中 CAS
操作的执行依赖于 Unsafe
类的方法。
注意:Unsafe
类中的所有方法都是 native
修饰的,也就是说 Unsafe
类中的方法都直接调用操作系统底层资源执行相应任务
private static final long VALUE = U.objectFieldOffset(AtomicInteger.class, "value");
private volatile int value;
@IntrinsicCandidate
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
v = getIntVolatile(o, offset);
} while (!weakCompareAndSetInt(o, offset, v, v + delta));
return v;
}
getInstance()
的性能对应用程序很关键。
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
classloader
机制来保证初始化 instance
时只有一个线程,它跟第 3 种方式不同的是:第 3 种方式只要 Singleton 类被装载了,那么 instance
就会被实例化(没有达到 lazy loading
效果),而这种方式是 Singleton
类被装载了,instance
不一定被初始化。因为 SingletonHolder
类没有被主动使用,只有通过显式调用 getInstance
方法时,才会显式装载 SingletonHolder
类,从而实例化 instance
。instance
很消耗资源,所以想让它延迟加载,另外一方面,又不希望在 Singleton
类加载时就实例化,因为不能确保 Singleton
类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化 instance
显然是不合适的。这个时候,这种方式相比 第 3 种方式 就显得很合理。public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
**经验之谈:**一般情况下,不建议使用第 1 种和第 2 种懒汉方式,建议使用第 3 种饿汉方式。只有在要明确实现 lazy loading 效果时,才会使用第 5 种登记方式。如果涉及到反序列化创建对象时,可以尝试使用第 6 种枚举方式。如果有其他特殊的需求,可以考虑使用第 4 种双检锁方式。
建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。
意图:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
主要解决:
何时使用:一些基本部件不会变,而其组合经常变化的时候。
如何解决:将变与不变分离开。
关键代码:
应用实例:
优点:
缺点:
使用场景:
注意事项:与工厂模式的区别是,建造者模式更加关注与零件装配的顺序。
我们假设一个快餐店的商业案例,其中,一个典型的套餐可以是一个汉堡(Burger)和一杯冷饮(Cold drink)。汉堡(Burger)可以是素食汉堡(Veg Burger)或鸡肉汉堡(Chicken Burger),它们是包在纸盒中。冷饮(Cold drink)可以是可口可乐(coke)或百事可乐(pepsi),它们是装在瓶子中。
我们将创建一个表示食物条目(比如汉堡和冷饮)的 Item
接口和实现 Item
接口的实体类,以及一个表示食物包装的 Packing
接口和实现 Packing
接口的实体类,汉堡是包在纸盒中,冷饮是装在瓶子中。
然后我们创建一个 Meal
类,带有 Item
的 ArrayList
和一个通过结合 Item 来创建不同类型的 Meal
对象的 MealBuilder
。BuilderPatternDemo
类使用 MealBuilder
来创建一个 Meal
。
Item.java
public interface Item {
public String name();
public Packing packing();
public float price();
}
Packing.java
public interface Packing {
public String pack();
}
Wrapper.java
public class Wrapper implements Packing {
@Override
public String pack() {
return "Wrapper";
}
}
Bottle.java
public class Bottle implements Packing {
@Override
public String pack() {
return "Bottle";
}
}
Burger.java
public abstract class Burger implements Item {
@Override
public Packing packing() {
return new Wrapper();
}
@Override
public abstract float price();
}
ColdDrink.java
public abstract class ColdDrink implements Item {
@Override
public Packing packing() {
return new Bottle();
}
@Override
public abstract float price();
}
VegBurger.java
public class VegBurger extends Burger {
@Override
public float price() {
return 25.0f;
}
@Override
public String name() {
return "Veg Burger";
}
}
ChickenBurger.java
public class ChickenBurger extends Burger {
@Override
public float price() {
return 50.5f;
}
@Override
public String name() {
return "Chicken Burger";
}
}
Coke.java
public class Coke extends ColdDrink {
@Override
public float price() {
return 30.0f;
}
@Override
public String name() {
return "Coke";
}
}
Pepsi.java
public class Pepsi extends ColdDrink {
@Override
public float price() {
return 35.0f;
}
@Override
public String name() {
return "Pepsi";
}
}
Meal.java
import java.util.ArrayList;
import java.util.List;
public class Meal {
private List<Item> items = new ArrayList<Item>();
public void addItem(Item item){
items.add(item);
}
public float getCost(){
float cost = 0.0f;
for (Item item : items) {
cost += item.price();
}
return cost;
}
public void showItems(){
for (Item item : items) {
System.out.print("Item : "+item.name());
System.out.print(", Packing : "+item.packing().pack());
System.out.println(", Price : "+item.price());
}
}
}
MealBuilder.java
public class MealBuilder {
public Meal prepareVegMeal (){
Meal meal = new Meal();
meal.addItem(new VegBurger());
meal.addItem(new Coke());
return meal;
}
public Meal prepareNonVegMeal (){
Meal meal = new Meal();
meal.addItem(new ChickenBurger());
meal.addItem(new Pepsi());
return meal;
}
}
BuilderPatternDemo.java
public class BuilderPatternDemo {
public static void main(String[] args) {
MealBuilder mealBuilder = new MealBuilder();
Meal vegMeal = mealBuilder.prepareVegMeal();
System.out.println("Veg Meal");
vegMeal.showItems();
System.out.println("Total Cost: " +vegMeal.getCost());
Meal nonVegMeal = mealBuilder.prepareNonVegMeal();
System.out.println("\n\nNon-Veg Meal");
nonVegMeal.showItems();
System.out.println("Total Cost: " +nonVegMeal.getCost());
}
}
Veg Meal
Item : Veg Burger, Packing : Wrapper, Price : 25.0
Item : Coke, Packing : Bottle, Price : 30.0
Total Cost: 55.0
Non-Veg Meal
Item : Chicken Burger, Packing : Wrapper, Price : 50.5
Item : Pepsi, Packing : Bottle, Price : 35.0
Total Cost: 85.5
原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。
我们将创建一个抽象类 Shape
和扩展了 Shape
类的实体类。下一步是定义类 ShapeCache
,该类把 shape 对象存储在一个 Hashtable
中,并在请求的时候返回它们的克隆。
PrototypePatternDemo
类使用 ShapeCache
类来获取 Shape
对象。
Shape.java
public abstract class Shape implements Cloneable {
private String id;
protected String type;
abstract void draw();
public String getType(){
return type;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Object clone() {
Object clone = null;
try {
clone = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
Rectangle.java
public class Rectangle extends Shape {
public Rectangle(){
type = "Rectangle";
}
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
Square.java
public class Square extends Shape {
public Square(){
type = "Square";
}
@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
Circle.java
public class Circle extends Shape {
public Circle(){
type = "Circle";
}
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
ShapeCache.java
import java.util.Hashtable;
public class ShapeCache {
private static Hashtable<String, Shape> shapeMap
= new Hashtable<String, Shape>();
public static Shape getShape(String shapeId) {
Shape cachedShape = shapeMap.get(shapeId);
return (Shape) cachedShape.clone();
}
// 对每种形状都运行数据库查询,并创建该形状
// shapeMap.put(shapeKey, shape);
// 例如,我们要添加三种形状
public static void loadCache() {
Circle circle = new Circle();
circle.setId("1");
shapeMap.put(circle.getId(),circle);
Square square = new Square();
square.setId("2");
shapeMap.put(square.getId(),square);
Rectangle rectangle = new Rectangle();
rectangle.setId("3");
shapeMap.put(rectangle.getId(),rectangle);
}
}
PrototypePatternDemo.java
public class PrototypePatternDemo {
public static void main(String[] args) {
ShapeCache.loadCache();
Shape clonedShape = (Shape) ShapeCache.getShape("1");
System.out.println("Shape : " + clonedShape.getType());
Shape clonedShape2 = (Shape) ShapeCache.getShape("2");
System.out.println("Shape : " + clonedShape2.getType());
Shape clonedShape3 = (Shape) ShapeCache.getShape("3");
System.out.println("Shape : " + clonedShape3.getType());
}
}
Shape : Circle
Shape : Square
Shape : Rectangle