Java基础

单例模式、工厂方法模式、代理模式、装饰者模式、观察者模式

2021-04-27  本文已影响0人  Merbng

设计模式六大原则

单例设计模式

public class Singleton{
    private static Singleton instance = new Singleton();
 
    private Singleton(){
 
    }
    public static Singleton getInstance(){
        return instance;
    }

}

这种方式在类创建时就完成了初始化,所以类加载慢,但是获取对象的速度快。
这种方式基于类加载机制,避免了多线程的同步问题。
在类加载的时候就完成初始化,没有达到懒加载的效果。如果从始至终未使用过这个实例,则会造成内存浪费

public Singleton{

    private static Singleton instance;

    private Singleton(){

    }
    public static Singleton getInstance(){
        if(instance==null){
            instance=new Singleton();
        }
        return instance;
    }

}

懒汉模式声明了一个静态对象,在用户第一次调用时初始化,这虽然节约了资源,但第一次加载时需要实例化,反应稍慢一些,
而且在多线程时不能正常工作

public class Singleton {
    private static Singleton instance;

    private Singleton(){}

    public static synchronized Singleton getInstance(){
        if(instance==null){
            instance=new Singleton();
        }
        return instance;
    }

}

这种写法能够在多线程中很好地工作,但是每次调用getInstance方法时都需要进行同步,这会造成不必要的同步开销
而且大部分时候我们是用不到同步的,所以不建议用这种模式。

public class Singleton{
    private volatile static Singleton instance;
    private Singleton(){}
    public static Singleton getInstance(){
        if(instance==null){
        synchronized(Singleton.class){
            if(instance==null){
                instance=new Singleton();
            }
        }
        }
        return instance;
    }

}

这种写法在getInstance方法中对Singleton进行了两次判空,第一次是为了不必要的同步,第二次是在Singleton等于null的情况下才创建实例。
在这里volatile会或多或少的影响性能,但考虑到程序的正确性,牺牲这点性能还是值得的。DCL的优点是资源利用率高。第一次执行genInstance时单例对象才被实例化,效率高。其缺点是第一次加载时反应稍慢一些,在高并发环境下也有一定的缺陷。
DCL虽然在一定程度上解决了资源的消耗和多余的同步、线程安全等问题,但其还是在某些情况会出现失效的问题,这里建议用静态内部类单例模式来替代DCL。

public class Singleton{

    private Singleton(){}
    public static Singleton getInstance(){
        return SingletonHolder.instance;
    }
    private static class SingletonHolder{
        private static final Singleton instance =new Singleton();
    }

}

第一次加载Singleton类时并不会初始化instance,只有第一次调用getInstance方法时虚拟机加载SingletonHolder并初始化instance
这样不仅能确保线程安全,也能保证Singleton类的唯一性。所以,推荐使用静态内部类单例模式。

public enum Singleton{
    INSTANCE;
    public void doSomeThing(){}
}

默认枚举实例创建是线程安全的,并且在任何情况下都是单例。
上面讲的几种单例模式的实现中,有一种情况下其会重新创建对象,那就是反序列化:将一个单例实例对象写到磁盘再读回来,从而获得了一个实例。
反序列化操作提供了readResolve方法,这个方法可以让开发人员控制对象的反序列化。
在上面几个方法事例中,如果要杜绝单例对象被反序列化时重新生成对象,就必须加入如下方法:

private Object readResolve() throws ObjectStreamException{
    return instance;
}

枚举单例的优点就是简单,其可读性并不是很高。

工厂方法模式

public abstract class Computer{
    public abstract void start();
}
public abstract class ComputerFactory{
    public abstract <T extends Computer> T createComputer(Class<T> clz);
}

public class GDComputerFactor extends ComputerFactory{
    @Override
    public <T extends Computer> T createComputer(Class<T> clz){
        Computer computer =null;
        String classname= clz.getName();
        try{
            computer =(Computer)Class.forName(classname).newInstance();
            
        }catch(Exception e){
            e.printStackTrace();
        }
        return (T)computer;
    }
}
//客户端调用
public class Client{

    public start void main(String[]args){
        ComputerFactory computerFactory =new GDComputerFactor();
        LenovoComputer mlenovoComputer = computerFactory.createComputer(LenovoComputer.class);
        mlenovoComputer.start();
    }

}

代理模式

//抽象主题类
public interface IShop{
    void buy();
}
//真实主题类
public class Merbng impelements IShop{
    @Override
    public void buy(){
        System.out.println("购买");
    }
}
//代理类
public class Purchasing impelements IShop{
    private IShop mShop;
    public Purchasing(IShop shop){
        mShop=shop
    }
        @Override
        public void buy(){
            mShop.buy();
        }
}
//客户端类
public class Client{
    public static void main(String[] args){
        IShop merbng =new Merbng();
        IShop purchasing =new Purchasing(merbng);
        purchasing.buy();
    }
}
public class DynamicPurchasing impelements InvocationHandler{
    private Object obj;
    public DynamicPurchasing(Object obj){
        this.obj =obj;
    }
    @Override
    public Object invoke(Object proxy,Method method,Object[] args)throws Throwable{
        Object result = method.invoke(obj,args);
        if(method.getName().equals("buy")){
            System.out.println("在买买买");
        }
        return result;
    }
}

在动态代理类中我们声明一个Object的引用,该引用指向被代理类,我们调用被代理类的具体方法在invoke()方法中执行。
接下来我们修改客户端类代码:

public class Client{
    public start void main(String[]args){
        //创建Merbng
        IShop merbng =new Merbng();
        //创建动态代理
        DynamicPurchasing mDynamicPurchasing =new DynamicPurchasing(merbng);
        //创建Merbng的ClassLoader
        ClassLoader loader =merbng.getClass().getClassLoader();
        //动态创建代理类
        IShop purchasing=(IShop)Proxy.newProxyInstance(loader,new Class[]{IShop.class},mDynamicPurchasing);
        purchasing.buy();
    }
}

调用Proxy.newProxyInstance()来生成动态代理类,调用purchasing的buy方法会调用DynamicPurchasing的invoke方法。

代理模式的优点主要有以下几点:

装饰者模式

//饮料抽象类
public abstract class Beverage{
    String description = "Unknown Beverage";
    public String getDescription(){
        return description;
    }
    //cost方法是用来返回饮料的价钱(需在具体类中自己实现)
    public abstract BigDecimal cost();
}
//深焙咖啡类(一种具体的饮料)
public class DarkRoast extends Beverage{
    public DarkRoast(){
        description="DarkRoast";
    }
    //实现cost方法,用来返回DarkRoast的价格
    @Override
    public BigDecimal cost(){
        return new BigDecimal("3.00");
    }
}
//低咖啡因咖啡类(一种具体的饮料)
public class Decaf extends Beverage{
    public Decaf(){
        description="Decaf"
    }

    //实现cost方法,用来返回Decaf的价格
    @Override
    public BigDecimal cost(){
        return new BigDecimal("4.00");
    }
}

//浓缩咖啡类(一种具体的饮料)
public class Espresso extends Beverage{
    public Espresso(){
        description="Espresso"
    }

    //实现cost方法,用来返回Espresso的价格
    @Override
    public BigDecimal cost(){
        return new BigDecimal("2.00");
    }
}
//调料装饰者抽象类(继承自饮料抽象类)
public abstract class CondimentDecorator extends Beverage{
    //所有的饮料装饰者类都必须重新实现getDescription()方法。这样才能够用递归的方式来得到所选饮料的整体描述
    public abstract String getDescription();
}

//摩卡调料类(继承自CondimentDecorator)
public class Mocha extends CondimentDecorator{
    //用一个实例变量记录饮料,也就是被装饰者
    Beverage beverage;
    public Mocha (Beverage beverage){
        this.beverage=beverage;
    }
    //在原来饮料的基础上添加上Mocha的描述(原来的饮料加入Mocha调料,被Mocha调料装饰)
    @Override
    public String getDescription(){
        return beverage.getDescription() + ", Mocha";
    }
    //在原来饮料的基础上加上Mocha的价格
    @Override
    public BigDecimal cost(){
        return new BigDecimal("0.2").add(beverage.cost());
    }
}
//豆浆调料类(继承自CondimentDecorator)
public class Soy extends CondimentDecorator{
    Beverage beverage;
    public Soy(Beverage beverage){
        this.beverage=beverage;
    }
    @Override
    public String getDescription(){
        reutrn beverage.getDescription()+",Soy";
    }
    @Override
    public BigDecimal cost(){
        return new BigDecimal("0.3").add(beverage.cost());
    }
}
//奶泡调料类(继承自CondimentDecorator)
public  class Whip extends CondimentDecorator{
    Beverage beverage;
    public Whip(Beverage beverage){
        this.beverage=beverage;
    }
    @Override
    public String getDescription(){
        return beverage.getDescription()+"Whip";
    }
    @Override
    public BigDecimal cost(){
        return new BigDecimal("0.4").add(beverage.cost());
    }
}

//咖啡馆(供应咖啡)
public class StarbuzzCoffee{
    public start void main(String[] args){
    //订一杯 Espresso(2.00)不需要调料,打印出描述和价钱
        Beverage beverage=new Espresso();
        System.out.println("Description:"+beverage.getDescription()+" $"+beverage.cost());
        //制造出一个DarkRoast(3.00)对象,用Mocha(0.2)装饰它,用第二个Mocha(0.2)装饰它,用Whip(0.4)装饰它,打印出描述和价格
        Beverage beverage2=new DarkRoast();
        beverage2=new Mocha(beverage2);
        beverage2=new Mocha(beverage2);
        beverage2=new Whip(beverage2);
        System.out.println("Description:"+ beverage2.getDescription()+"$"+beverage2.cost());
        //再来一杯调料为豆浆(Soy 0.3)、摩卡(Mocha 0.2)、奶泡(Whip 0.4)的 Decaf(低咖啡因咖啡 4.0)。打印他的描述和价格
        Beverage beverage3 =new Decaf();
        beverage3=new Soy(beverage3);
        beverage3=new Mocha(beverage3);
        beverage3=new Whip(beverage3);
        System.out.println("Description:"+beverage3.getDescription()+"$"+beverage3.cost());
        
    }
}
运行结果:
Description: Espresso $2.00
Description: DarkRoast,Mocha,Mocha,Whip $3.9
Description: Decaf,Soy,Mocha,Whip $4.90


从以上,我们可以知道,当我们使用继承,导致子类膨胀,我们不想增加很多子类的情况下,将具体功能职责划分,同时继承装饰者超类,动态给一个对象添加一些额外的职责便实现了我们的装饰者模式。

观察者模式

关于观察者模式这种发布-订阅的形式,我们可以拿微信公众号来举例。
假设微信用户就是观察者,微信公众号是被观察者,有多个微信用户关注了同一个公众号,
当这个公众号更新时就会通知这些订阅的微信用户,代码实现如下:

//抽象观察者  里面定义了 一个更新的方法
public interface Observer{
    public void update(String message);
}
//具体观察者, 微信用户是观察者, 里面实现了更新的方法
public class WeiXinUser impelements Observer{
    //微信用户名
    private String name;
    public WeiXinUser(String name){
        this.name =name;
    }
    @Override
    public void update(String message){
        System.out.println(name+"-"+message);
    }

}
//抽象被观察者  提供了attach、detach、notify 三个方法
public interface Subject{
    //增加订阅者
    public void attach(Observer observer);
    //删除订阅者
    public void detach(Observer observer);
    //通知订阅者更新信息
    public void notify(String message);
}
//具体被观察者,微信公众号就是具体被观察者,里面存储了订阅该公众号的微信用户,并实现了抽象主题中的方法
public class SubscriptionSubject impelements Subject{
    //存储订阅公众号的微信用户
    private List<Observer> weixinUserList = new ArrayList<Observer>();
    
    @Override
    public void attach(Observer observer){
        weixinUserList.add(observer);
    }
    @Override
    public void detach(Observer observer){
        weixinUserList.remove(observer);
    }
    @Override
    public void notify(String message){
        for(Observer observer : weixinUserList){
            observer.notify(message);
        }
    }
}
//客户端调用
public class Client{
    public static void main(String[]args){
        SubscriptionSubject mSubscriptionSubject =new SubscriptionSubject();
        //创建微信用户
        WeiXinUser user1 =new WeiXinUser("用户1");
        WeiXinUser user1 =new WeiXinUser("用户2");
        WeiXinUser user1 =new WeiXinUser("用户3");
        //订阅公众号
        mSubscriptionSubject.attach(user1);
        mSubscriptionSubject.attach(user2);
        mSubscriptionSubject.attach(user3);
        //公众号更新发出消息给订阅的微信用户
        mSubscriptionSubject.notify("有新文章了");
        
    }
}
上一篇 下一篇

猜你喜欢

热点阅读