Java中常见的设计模式总结

一、单例设计模式

单例设计模式:一个类只允许产生一个实例化对象。

思路:

首先,其它类可以实例化某个类的前提是该类的构造方法对它们可见,也就是说这个类的构造方法是public属性。而现在为了让其它类不能够实例化这个类,我们就需要将这个类的构造方法私有化

其次,由于其它类已经不能通过调用这个类的构造方法来实例化对象了,而它们又想要使用这个类的对象,我们肯定得在这个类中设置一个方法来返回这个类的实例化对象。当其它类想要使用这个类的实例化对象的时候,只需要调用这个类的提供实例化对象的方法就可以了。那么现在问题就是如何设置可以提供实例化对象的方法的访问属性了:我们知道,如果想要调用某个类的普通方法,必须通过对象来调用。而现在,由于其它类不能直接实例化某个类,所以通过对象访问这个类的普通方法就不可行了。那么怎么办呢? 我们又知道,被static关键字修饰的方法可以不依赖于对象便可以访问,所以,为了让其它类可以得到某个类的实例化对象,我们必须在这个类中提供一个静态方法,让其它类可以直接通过这个类的类名.静态方法名来获取该类的实例化对象。


补充说明:

关于static方法的两点说明:

1. 所有被static关键字修饰的方法不允许调用非static方法,也不允许访问非static属性【那么对于标识本类实例化对象的属性变量我们就应该用static关键字修饰啦】

2. 非static方法可以访问static属性或方法

那么我们可以通过如下步骤来实现:

  1. 将该类的构造方法私有化,以防止在该类之外时调用构造方法实例化对象。
  2. 在该类的内部提供一个静态方法,当类的引用不为空时实例化对象并将其返回,否则直接返回,以此来获得该类的实例化对象。

通常我们所知道的单例设计模式实现有两种:饿汉式、懒汉式。所谓饿汉式就是直接实例化好对象,一般会用final关键字修饰,以防止该实例化对象被修改,所以不存在线程安全问题;而懒汉式是在程序要用对象时才实例化,否则为null,所以存在线程安全问题。

那么什么是线程安全问题呢?用这里的懒汉式实现单例模式来说就是,在多线程的环境下,如果唯一的实例化对象还没有创建时,那么如果有两个或多个线程同时检测到这种情况,它们就会各自创建一个实例化对象,这样就会破坏单例的实现原则,所以我们需要提供互斥锁来解决此类问题。


了解完单例设计模式之后,我们来看一下单例设计模式的几种实现方法:

1.饿汉式

//饿汉式
class Singleton {
    private final static Singleton singleton = new Singleton();

    //私有的构造方法
    private Singleton() {

    }

    public static Singleton getSingleton() {
        return singleton;
    }

    public void print() {
        //如果两次得到的结果一样,表示得到的是同一个实例化对象
        System.out.println(Singleton.getSingleton() == Singleton.getSingleton());
    }
}


public class TestSingleton {
    public static void main(String[] args) {
        Singleton singleton = null;
        singleton = Singleton.getSingleton();
        singleton.print();
    }
}

总结:由于类加载时就完成了对象的实例化,避免了线程安全问题,但是如果程序一直没有用到该实例化对象便会造成内存的浪费。

2.懒汉式(同步方法)

//懒汉式(同步方法)
class Singleton {
    private static Singleton singleton;

    private Singleton() {

    }

    public static synchronized Singleton getSingleton() { //同步方法
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }

    public void print() {
        //如果两次得到的结果一样,表示得到的是同一个实例化对象
        System.out.println(Singleton.getSingleton() == Singleton.getSingleton());
    }
}

public class TestSingleton {
    public static void main(String[] args) {
        Singleton singleton = null;
        singleton = Singleton.getSingleton();
        singleton.print();
    }
}

总结:在静态方法前加synchronized可以保证同一时间只有一个线程在使用该静态方法,避免了线程安全问题,但是如果某一个线程已经实例化好对象后,其他线程本可以直接获取该对象而不用等到这个线程释放了锁然后自己再加锁获取,所以此实现方式效率不高。 

 3.懒汉式(双重检查)

//懒汉式(双重检查)
class Singleton {
    private static Singleton singleton;

    private Singleton() {

    }

    public static Singleton getSingleton() { //双重检查
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }

    public void print() {
        //如果两次得到的结果一样,表示得到的是同一个实例化对象
        System.out.println(Singleton.getSingleton() == Singleton.getSingleton());
    }
}

public class TestSingleton {
    public static void main(String[] args) {
        Singleton singleton = null;
        singleton = Singleton.getSingleton();
        singleton.print();
    }
}

总结:双重检查是对同步方法的改进,此时不将synchronized加在方法上,而是在方法内部,可以保证当多个线程同时调用该方法时,实例化对象只用执行一次,当判断完singleton!=null后其它线程可以直接返回已实例化好的对象,效率明显提高。


适用场景:

1. 需要频繁访问(实例化)和销毁的对象。

2. 创建对象时耗时过多或耗资源过多,但又被经常用到的对象。

3. 频繁访问数据库或文件的对象。 


二、工厂设计模式

工厂设计模式又分为简单工厂模式、工厂方法模式和抽象工厂模式

  • 简单工厂模式

简单工厂模式:一个接口(抽象产品类),多个接口的实现类(具体产品类),一个工厂类(用来实例化接口的实现类)

package com.myself.test;

//接口
interface Goods {
    void buy();
    void receive();
}

//接口的实现类
class Book implements Goods {

    public void buy() {
        System.out.println("在网上买了一本书");
    }

    public void receive() {
        System.out.println("书收到了");
    }
}

//接口的实现类
class Pen implements Goods {

    public void buy() {
        System.out.println("在网上买了一支笔");
    }

    public void receive() {
        System.out.println("笔收到了");
    }
}

//实例化 接口的实现类 的工厂
class Factory {
    public static Goods getInstance(String name) {
        Goods goods = null;
        if ("book".equals(name)) {
            goods = new Book();
        } else if ("pen".equals(name)) {
            goods = new Pen();
        }
        return goods;
    }
}

public class Test1 {
    public static void main(String[] args) {
        Goods goods = Factory.getInstance("pen");
        if(goods !=null){
            goods.buy();
            goods.receive();
        }
    }
}

总结:

优点:简单工厂模式将类的实例化交给了工厂,易于解耦;

缺点:但添加新产品时需要修改工厂,违背了OCP原则。 

  • 工厂方法模式

工厂方法模式:一个抽象产品类,多个具体产品类,一个抽象工厂类,多个具体工厂类

package com.myself.test;

//抽象产品
interface Drink {
    void drinking();
}

//具体产品
class OrangeJuice implements Drink {

    public void drinking() {
        System.out.println("喝橙汁");
    }
}

//具体产品
class MineralWater implements Drink {

    public void drinking() {
        System.out.println("喝矿泉水");
    }
}

//抽象工厂
interface ProdactionFactory {
    Drink createDrink();
}

//具体工厂
class OrangeFactory implements ProdactionFactory {

    public Drink createDrink() {
        return new OrangeJuice();
    }
}

//具体工厂
class WaterFactory implements ProdactionFactory {

    public Drink createDrink() {
        return new MineralWater();
    }
}

public class Test2 {
    public static void main(String[] args) {
        ProdactionFactory factory = new OrangeFactory();
        Drink drink = factory.createDrink();
        drink.drinking();
    }
}

总结: 

优点:将类的实例化交给了具体的工厂类,降低了代码的耦合度,也实现了OCP原则(每次有新的产品时,不需修改原代码)

缺点:每次有具体产品时,都要一个具体工厂,增加了代码量;但当有抽象产品时必须修改抽象工厂,将会破坏OCP原则

  • 抽象工厂模式

抽象工厂模式:多个抽象产品,多个具体产品,抽象工厂(声明一组抽象产品的方法),具体工厂(生成一组具体产品)

package com.myself.test;

//抽象产品1
interface Cup{
    void drinkingWater();
}

//抽象产品1对应的具体产品
class ThermonCup implements Cup{

    public void drinkingWater() {
        System.out.println("用保温杯喝水");
    }
}

//抽象产品2
interface Fruit{
    void eat();
}

//抽象产品2对应的具体产品
class Apple implements Fruit{

    public void eat() {
        System.out.println("吃苹果");
    }
}

//抽象工厂
interface IFactory{
    Cup createCup();
    Fruit createFruit();
}

//具体工厂(返回一组具体产品)
class CFFactory implements IFactory{

    public Cup createCup() {
        return new ThermonCup();
    }

    public Fruit createFruit() {
        return new Apple();
    }
}

public class Test3 {
    public static void main(String[] args) {
        IFactory factory=new CFFactory();
        Cup cup=factory.createCup();
        cup.drinkingWater();
        Fruit fruit=factory.createFruit();
        fruit.eat();
    }
}

总结:

优点:  能很好的生产多类产品

缺点:当新增加抽象产品时既要修改抽象工厂,也要修改具体工厂,违背了OCP原则

三、代理设计模式

代理设计模式:两个子类共同实现一个接口,其中一个子类负责真实业务的实现,另一个子类辅助完成真实业务。

举例:就拿我们平常在网上买东西一样,当买家有意愿买某种东西并向卖家付完款后我们并不能立即收到物品,而是卖家先将物品打包好交到快递员手上,然后快递员根据买家提供的收货地址将物品送到并通知买家取快递。在这个过程中,所谓真实的业务就是买家买东西,而卖家和快递员所做的事就是辅助完成买家收到物品。

package com.myself.test;

interface Flowers {
    void sendingFlowers();
}

//真实业务
class Children implements Flowers {

    public void sendingFlowers() {
        System.out.println("子女通过快递送花给父母");
    }
}

class Courier implements Flowers {
    private Flowers flowers;

    public Courier(Flowers flowers) {
        this.flowers = flowers;
    }

    public void sendingFlowers() {
        System.out.println("卖家将花交给快递员");
        System.out.println("快递员根据买家所填收货地址进行派送");
        flowers.sendingFlowers();
        System.out.println("父母收到子女送的花");
    }
}

public class Test4 {
    public static void main(String[] args) {
        Flowers flowers = new Children();
        new Courier(flowers).sendingFlowers();
    }
}

总结:所有真实业务都会有一个辅助类共同完成。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值