学习目标:
常用的几种design pattern
利弊分别是什么?
具体举个例子?
学习内容:
创建型模式:
1、 单例模式singleton
2、 工厂模式factory
3、 建造者模式builder
4、 原型模式prototype
学习时间:
1、 周一至周五晚上 7 点—晚上9点学习产出:
1、 视频10+个 2、技术笔记 3 篇1.singleton单例模式
- 饿汉式实例化
Eager Initialization,使用static定义这个instance,类一加载就生成这个instance。
package Singleton;
public class SingletonA {
private static final SingletonA instance=new SingletonA();// the only copy
//1.private constructor
private SingletonA () {
if(instance!=null) {
throw new RuntimeException("cannot created");
}
}
//2.method: how to get instance: class.getInstance()
public static SingletonA getInstance(){
//3.return the only copy of the class
return instance;
}
}
测试类:
package Singleton;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class SingletonTest {
public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException {
SingletonA a=SingletonA.getInstance();
//cast to reflection:
Constructor<SingletonA> c= (Constructor<SingletonA>) SingletonA.class.getDeclaredConstructors()[0];
c.setAccessible(true);//无视私有构造器,
SingletonA b=c.newInstance(); //通过反射new第二个instance
System.out.println(a==b);//验证是否为同一个 //return false
}
}
测试时,故意利用反射破坏单例模式【进攻】。
c.setAccessible(true);//无视私有构造器,
SingletonA b=c.newInstance(); //通过反射new第二个instance
System.out.println(a==b);//验证是否为同一个,结果预测会return false。
此时【防守】,
if(instance!=null) {
throw new RuntimeException(“cannot created”);
}发挥作用,抛出异常,防止单例模式被破坏。
Eager Initialization缺点:If never used, waste resources.
- 懒汉式实例化
package Singleton;
public class SingletonB {
private static SingletonB instance=null;//the only copy
//1.private constructor
private SingletonB () {
}
//2.method: how to get instance: class.getInstance()
public static SingletonB getInstance(){
if(instance==null) {
instance = new SingletonB (); //2 threads will create 2 objs
}
//3.return the only copy of the class
return instance;
}
}
Lazy Initialization优点:solve the probelm of wasting resources.
Lazy Initialization缺点:not thread safe. 2 threads will create 2 objects.
- 加锁
package Singleton;
public class SingletonC {
private static SingletonC instance=null;// the only copy
//1.private constructor
private SingletonC () {
}
//2.method: how to get instance: class.getInstance()
public synchronized static SingletonC getInstance(){ //syn, safe,but wait others.
if(instance==null) {
instance = new SingletonC ();
}
//3.return the only copy of the class
return instance;
}
}
加锁后线程安全了,但出现了互相等的情况,这个有必要吗?如果只是在new的阶段,两个threads就苦等对方,造成代码性能下降。
- volatile关键字+Double Check Lock
best solution
package Singleton;
public class SingletonD {
private volatile static SingletonD instance=null;// the only copy
//1.private constructor
private SingletonD () {
}
//2.method: how to get instance: class.getInstance()
public static SingletonD getInstance(){ //2 threads
if(instance==null) { //null
synchronized (SingletonD.class) {//2 threads race for lock 这里锁class/方法都可以
if (instance == null) {//1 thread get the lock //then 2nd thread get not null.
instance = new SingletonD(); //1st create
}
}//1 st exist
}
//3.return the only copy of the class
return instance;
}
}
instance = new SingletonD()不是原子性操作,实际上有三步操作:
1。分配内存空间
2。执行构造函数,初始化对象
3。把对象reference指向这个空间
会出现指令重排,先132,或者先321
如果出现线程B,看到reference指向空间了,
以为instance!=null直接return instance;
实际上,instance还没有完成构造
solution:添加一个volatile关键字,从main memory快速读取,避免编译器指令重排(Compiler Instruction Reordering)
- enum枚举
17:30利用enum枚举本身的特性,防止反射破坏单例
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
class Test{
public static void main(String[] args) {
EnumSingle instance1=EnumSingle.INSTANCE;
EnumSingle instance2=EnumSingle.INSTANCE;
System.out.println(instance1);
System.out.println(instance2);
}
}
枚举中通过jad反编译发现,实际上不存在无参构造,无法被反射破坏。
注解:jad反编译工具:Jad uses JAVA class name as an output file name。
2.factory工厂模式
【狂神说Java】通俗易懂的23种设计模式
核心思想:创建者和调用者分离
解决问题:生产具体产品。
- 简单工厂/静态工厂
public interface Car {
void name();
}
public class Tesla implements Car {
@Override
public void name() {
System.out.println("Tesla!");
}
}
public class Wuling implements Car {
@Override
public void name() {
System.out.println("Wuling!");
}
}
public class CarFactory {
public static Car getCar(String car) {
if(car.equals("Wuling")){
return new Wuling();
}else if(car.equals("Tesla")){
return new Tesla();
}else{
return null;
}
}
}
public class Consumer {
public static void main(String[] args) {
//不用new,只需要从工厂买
Car car1=CarFactory.getCar("Wuling");
Car car2=CarFactory.getCar("Tesla");
car1.name();
car2.name();
}
}
优点:简单。消费者买车无需关心各种车型的实现细节。
缺点:不符合开闭原则(open to extension, close to modification)
如果需要增加大众汽车,就必须修改工厂模式中的代码。
- 工厂方法
再为每种车型造一个工厂:
public interface Car {
void name();
}
public interface CarFactory {
Car getCar();
}
public class Tesla implements Car {
@Override
public void name() {
System.out.println("Tesla!");
}
}
public class TeslaFactory implements CarFactory {
@Override
public Car getCar() {
return new Tesla();
}
}
public class Wuling implements Car {
@Override
public void name() {
System.out.println("Wuling!");
}
}
public class WulingFactory implements CarFactory{
@Override
public Car getCar() {
return new Wuling();
}
}
public class Consumer {
public static void main(String[] args) {
Car car1= new TeslaFactory().getCar();
Car car2=new WulingFactory().getCar();
car1.name();
car2.name();
}
}
工厂方法是为Tesla、Wuling每个产品再造各自的工厂。
优点:若新增加大众汽车,可以拓展Dazhong类,DazhongFactory类,无需修改工厂模式中的代码。
缺点:每增加一种汽车,增加两个类,类多。
符合设计原则:工厂方法
实际应用:简单工厂
2.abstract factory抽象工厂模式
解决的问题:生产产品族。
from:【狂神说Java】通俗易懂的23种设计模式
手机产品接口:
package com.kuang.designPattern.abstractFactory;
public interface PhoneProduct {
void start();
void shutdown();
void call();
void sendSMS();
}
路由产品接口
public interface RouterProduct {
void start();
void shutdown();
void openwifi();
void setting();
}
工厂接口
public interface ProductFactory {
PhoneProduct producePhoneProduct();
RouterProduct produceRouterProduct();
}
小米工厂:
public class XiaomiFactory implements ProductFactory{
@Override
public PhoneProduct producePhoneProduct() {
return new XiaomiPhone();
}
@Override
public RouterProduct produceRouterProduct() {
return new XiaomiRouter();
}
}
public class XiaomiPhone implements PhoneProduct {
@Override
public void start() {
System.out.println("start Xiaomi Phone");
}
@Override
public void shutdown() {
System.out.println("shutdown Xiaomi Phone");
}
@Override
public void call() {
System.out.println("call from Xiaomi Phone");
}
@Override
public void sendSMS() {
System.out.println("send SMS from Xiaomi Phone");
}
}
public class XiaomiRouter implements RouterProduct{
@Override
public void start() {
System.out.println("start Xiaomi Router");
}
@Override
public void shutdown() {
System.out.println("shutdown Xiaomi Router");
}
@Override
public void openwifi() {
System.out.println("openwifi for Xiaomi Router");
}
@Override
public void setting() {
System.out.println("set for Xiaomi Router");
}
}
华为工厂:
public class HuaweiFactory implements ProductFactory{
@Override
public PhoneProduct producePhoneProduct() {
return new HuaweiPhone();
}
@Override
public RouterProduct produceRouterProduct() {
return new HuaweiRouter();
}
}
public class HuaweiPhone implements PhoneProduct{
@Override
public void start() {
System.out.println("start Huawei Phone");
}
@Override
public void shutdown() {
System.out.println("shutdown Huawei Phone");
}
@Override
public void call() {
System.out.println("call from Huawei Phone");
}
@Override
public void sendSMS() {
System.out.println("sendSMS from Huawei Phone");
}
}
public class HuaweiRouter implements RouterProduct{
@Override
public void start() {
System.out.println("start Huawei Router");
}
@Override
public void shutdown() {
System.out.println("shutdown Huawei Router");
}
@Override
public void openwifi() {
System.out.println("openwifi for Huawei Router");
}
@Override
public void setting() {
System.out.println("setting for Huawei Router");
}
}
public class Client {
public static void main(String[] args) {
System.out.println("=====Xiaomi Product====");
//xiaomi factory
XiaomiFactory xiaomiFactory = new XiaomiFactory();
PhoneProduct xPhone = xiaomiFactory.producePhoneProduct();
xPhone.start();
xPhone.call();
RouterProduct xRouter = xiaomiFactory.produceRouterProduct();
xRouter.start();
xRouter.openwifi();
System.out.println("=====Huawei Product====");
//Huawei factory
HuaweiFactory huaweiFactory=new HuaweiFactory();
PhoneProduct hPhone = huaweiFactory.producePhoneProduct();
hPhone.start();
hPhone.call();
RouterProduct hRouter=huaweiFactory.produceRouterProduct();
hRouter.start();
hRouter.openwifi();
}
}
抽象工厂是“工厂的工厂”。由工厂接口拓展出小米工厂、华为工厂。
代码中,产品族分为小米族、华为族,产品分为手机、路由。
优点:无需关心代码实现细节,将一个系列的产品统一到一起创建
缺点:拓展新的产品难,增加了系统抽象性和理解难度。
3.builder建造者模式
抽象建造者
package com.kuang.builder;
public abstract class Builder {
abstract void buildDiji();
abstract void buildGangjing();
abstract void buildDianxian();
abstract void buildFenshua();
abstract Product getProduct();
}
指挥者
public class Director {
//核心顺序,由director定
public Product build(Builder builder){
builder.buildDiji();
builder.buildDianxian();
builder.buildGangjing();
builder.buildFenshua();
return builder.getProduct();
}
}
房子
public class Product {
private String diji;
private String gangjing;
private String dianxian;
private String fenshua;
public String getDiji() {
return diji;
}
public void setDiji(String diji) {
this.diji = diji;
}
public String getGangjing() {
return gangjing;
}
public void setGangjing(String gangjing) {
this.gangjing = gangjing;
}
public String getDianxian() {
return dianxian;
}
public void setDianxian(String dianxian) {
this.dianxian = dianxian;
}
public String getFenshua() {
return fenshua;
}
public void setFenshua(String fenshua) {
this.fenshua = fenshua;
}
@Override
public String toString() {
return "Product{" +
"diji='" + diji + '\'' +
", gangjing='" + gangjing + '\'' +
", dianxian='" + dianxian + '\'' +
", fenshua='" + fenshua + '\'' +
'}';
}
}
具体执行者
public class Worker extends Builder{
private Product product;
public Worker() {
product = new Product();//工人自己建产品
}
@Override
void buildDiji() {
product.setDiji("地基");
System.out.println("地基");
}
@Override
void buildGangjing() {
product.setGangjing("钢筋");
System.out.println("钢筋");
}
@Override
void buildDianxian() {
product.setDianxian("电线");
System.out.println("电线");
}
@Override
void buildFenshua() {
product.setFenshua("粉刷");
System.out.println("粉刷");
}
@Override
Product getProduct() {
return product;
}
}
测试类
public class Test {
public static void main(String[] args) {
Director director=new Director();
//director指挥worker,建造产品,
Product build = director.build(new Worker());
System.out.println(build.toString());
}
}
Builder是一个抽象建造者,Worker extends Builder改写了各个方法。
Product类中主要写好getter和setter。最重要的工作的顺序,先打地基还是先铺电线是由director定的。
4.prototype原型模式
原型这个类,实现了Cloneable接口,new一个instance之后,副本就可以通过clone方法复制。
public class Video implements Cloneable{
String name;
Date date;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public Video() {
}
public Video(String name, Date date) {
this.name = name;
this.date = date;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
@Override
public String toString() {
return "Video{" +
"name='" + name + '\'' +
", date=" + date +
'}';
}
}
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Date date=new Date();
Video v1=new Video("kuangshenshuo",date);
Video v2=(Video)v1.clone();//副本
System.out.println("v1=>"+v1);
System.out.println("v2=>"+v2);
System.out.println("=======");
date.setTime(22222);//修改日期
System.out.println("v1=>"+v1);
System.out.println("v2=>"+v2);//副本日期也跟着修改
}
}
问题:修改原型的日期,那么副本的日期也跟着修改,这是浅拷贝。
要实现深拷贝,需要修改clone方法,将这个对象的属性也进行克隆。
@Override
protected Object clone() throws CloneNotSupportedException {
Object obj=super.clone();
Video v=(Video) obj;
v.date=(Date)this.date.clone();//将这个对象的属性也进行克隆
return obj;
}
这样即便原型v1的日期变化了,v2作为副本,有自己独立的日期,不会跟着变化。
Thank you for reading !