常用的设计模式部分
双重检查锁实现单例模式
public Class Sington{
private static volatile sington ;
private Sington{
}
public Sington getInstance(){
if(sington==null){
synchronied(Sington.class){
if(sington==null){
return new Sington();//3
}
}
}
return sington;
}
}
// 缺点 还是可以通过反射创建多个实例
// 优点 并发安全
- 为什么getInstance方法不通过synchronized关键字实现
实现同步的确可以通过在方法中添加synchronized关键字
但是这很低效 每次调用这个方法都得承受内部锁开销 无论是否已经创建实例 并且同步只需要
在第一次调用这个方法时才需要 - 为什么要使用volatile修饰
不是两个if多重检验已经够了 为什么还要加个volatile关键字?
这得从将getInstance方法翻译为汇编语言说起
public Sington getInstance(){
if(sington==null){
instance =new Sington();//3
}
return sington;
}
;asm code generated for getInstance
054D20B0 mov eax,[049388C8] ;load instance ref
054D20B5 test eax,eax ;test for null
054D20B7 jne 054D20D7 ;转移语句 如果不相等则跳转
054D20B9 mov eax,14C0988h ;mov指令的意思是将14C0988h给eax
054D20BE call 503EF8F0 ;allocate memory
054D20C3 mov [049388C8],eax ;store pointer in
;instance ref. instance
;non-null and ctor
;has not run
054D20C8 mov ecx,dword ptr [eax]
054D20CA mov dword ptr [ecx],1 ;inline ctor - inUse=true;
054D20D0 mov dword ptr [ecx+4],5 ;inline ctor - val=5;
054D20D7 mov ebx,dword ptr ds:[49388C8h]
054D20DD jmp 054D20B0
054D20C3对应赋值 注意此时只是将一个没有进行初始化的对象赋给 [049388C8]
C8到D7是执行构造器的代码
如果此代码在执行 C3 行后且在完成该构造函数前被另一个线程中断,
另一个线程执行if语句后又被主线程抢去 主线程执行完毕返回 初始化完成的单例对象
此时线程2 将返回一个未初始化的对象
则双重检查锁定就会失败。
可以使用volatile修饰该变量 volatile保证了可见性 并禁止了重排序 保证同步 及使用volatile修饰的变量是按顺序执行的
单例模式使用static字段的实例实现
饿汉式
class Singleton
{
private Vector v;
private boolean inUse;
private static Singleton instance = new Singleton();
private Singleton()
{
v = new Vector();
inUse = true;
//...
}
public static Singleton getInstance()
{
return instance;
}
}
代理模式
什么是代理模式 访问者不直接访问被访问者 而是通过代理
代理最终会将访问请求转发给真正的被访问者 他可以在转发访问之前和之后加入特定的逻辑
//在每天午夜0点到次日6点之间 request调用不被接受
public class ServiceControlSubjectProxy implements ISubject{
private static final Logger=LogFactory.getLog(ServiceControlSubjectProxy.class);
private Isubject subject;
public ServiceControlSubjectProxy(ISubject s){
this.subject=s;
}
public String request(){
TimeOfDay startTime=new TimeOfDay(0,0,0);
TimeOfDay endTime=new TimeOfDay(5,59,59);
TimeOfDay currentTime=new TimeOfDay();
if(currentTime.isAfter(startTime) && current.isBefore(endTime))
return null;
Sting originalRestult=subject.request();
return "Proxy:"+originalResult;
}
}
ISubject target=new SubjectImpl();
ISubject finalSubject=new ServiceControlSubjectProxy(target);
finalSubject.request();
//为什么SubjectImpl 和 ServiceControlSubjectProxy都实现了相同的接口 这是因为
//ServiceControlSubjectProxy作为代理对象必须实现被代理对象的同一个接口才具有相同功能
上诉静态代理存在弊端
如果存在Joinpoint相同(request()方法和执行横切逻辑一样) 但是对应的目标对象类型是不一样的时候
就要为不同对象实现不同的代理对象 尽管其他都一样
工厂模式
简单工厂 现在有一个工厂生产手机 生产的手机有苹果,和小米
怎样设计这个类并实现他呢
- 需要一个手机接口
- 苹果,小米类分别实现这个接口
- 创建一个工厂类
- 该类的某个方法返回值是手机接口 将根据参数返回具体的实例
public interface Phone {
void make();
}
public class MiPhone implements Phone {
public MiPhone() {
this.make();
}
@Override
public void make() {
// TODO Auto-generated method stub
System.out.println("make xiaomi phone!");
}
}
public class IPhone implements Phone {
public IPhone() {
this.make();
}
@Override
public void make() {
// TODO Auto-generated method stub
System.out.println("make iphone!");
}
}
public class PhoneFactory {
public Phone makePhone(String phoneType) {
if(phoneType.equalsIgnoreCase("MiPhone")){
return new MiPhone();
}
else if(phoneType.equalsIgnoreCase("iPhone")) {
return new IPhone();
}
return null;
}
}
但是这种写法的弊端 是实现类和工厂耦合
`` 并且这上述方式不符合开闭原则(对于扩展是开放的 对于修改是关闭的)上诉代码 要再添加一个HUAWEI手机 就得修改if else语句
当然可以通过反射解决
public static Phone getPhone(Class c){
Phone phone=null;
try{
phone=(Phone)Class.forName(c.getName()).newInstance();
}catch(Exception e)
{
e.printSackTrace();
}
return phone;
}
换句话说 如果要优化简单工厂模式 那么就得 就得删掉if else
怎么删掉 根据时间换空间 空间换时间的思路 必然要构造多个类
通过定义一个抽象工厂接口 将具体的生产的类交给具体的抽象接口派生类
这些派生类去构造对象
工厂方法模式
-
定义一个抽象工厂接口 即产品的生产接口 它将生产任务交给不同的派生类工厂
-
定义具体的派生类工厂 实现抽象工厂接口
public interface AbstractFactory {
Phone makePhone();
}
public class XiaoMiFactory implements AbstractFactory{
@Override
public Phone makePhone() {
return new MiPhone();
}
}
public class AppleFactory implements AbstractFactory {
@Override
public Phone makePhone() {
return new IPhone();
}
}
public class Demo {
public static void main(String[] arg) {
AbstractFactory miFactory = new XiaoMiFactory();
AbstractFactory appleFactory = new AppleFactory();
miFactory.makePhone(); // make xiaomi phone!
appleFactory.makePhone(); // make iphone!
}
}
这样就避免了简单工厂方法中的耦合
题外话:
但是为什么 这里不使用泛型??
使用泛型如果想生产另一类 直接改变<>中的内容就好了 可能这回导致另一个问题 泛型被擦除了怎么办
抽象工厂模式
上面两种模式不管工厂怎么细分 都只是针对一类产品 如果要生产另外的产品
只需在抽象工厂接口中添加新的方法 用于生成新的类
public interface AbstractFactory {
Phone makePhone();
PC makePC();
}
public class XiaoMiFactory implements AbstractFactory{
@Override
public Phone makePhone() {
return new MiPhone();
}
@Override
public PC makePC() {
return new MiPC();
}
}
public class AppleFactory implements AbstractFactory {
@Override
public Phone makePhone() {
return new IPhone();
}
@Override
public PC makePC() {
return new MAC();
}
}
参考
https://blog.csdn.net/chenchaofuck1/article/details/51702129 [Java单例模式中双重检查锁的问题]
https://www.cnblogs.com/yssjun/p/11102162.html [设计模式之工厂模式(factory pattern)]