Java 常见的9种设计模式(一)

单例模式、简单工厂模式、工厂方法和抽象工厂模式、代理模式

Posted by LANY on April 9, 2019

Java 常见的9种设计模式(一)

Java中的设计模式总体分为三大类:

创建型模式:工厂方法模式、单例模式、抽象工厂模式、建造者模式、原型模式。

结构型模式: 适配器模式、装饰器模式、代理模式、门面模式、桥接模式、组合模式、享元模式。

行为性模式: 策略模式、模版方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、终结者模式、解释器模式。

但在日常的开发中,最常用的设计模式却只有9中:

单例模式简单工厂模式(不在23种设计模式中)、工厂方法和抽象工厂模式代理模式命令模式策略模式门面模式桥接模式观察者模式

在本次文章中,先介绍单例模式简单工厂模式(不在23种设计模式中)、工厂方法和抽象工厂模式代理模式

单例模式

单例,顾名思义,单一唯一的实例。在某些时候,过多的创建某个类的实例不仅没有意义,有时候还会造成系统的性能下降。这个时候我们可以利用单例模式来有效避免这种问题。

如果一个类始终只能创建一个实例,那么这个类就被称为单例类,这种设计模式被称为单例模式。

单例模式的伪代码如下:

/**
 * Created by lany .
 */
public class SingleTonOne {

    private static final SingleTonOne singleTonOne = new SingleTonOne();


    private SingleTonOne(){
        System.out.println("SingleTonOne");
    }

    private static  SingleTonOne getInstance(){
        return singleTonOne;
    }

    public static void main(String[] args){
        SingleTonOne single1 = SingleTonOne.getInstance();
        SingleTonOne single2 = SingleTonOne.getInstance();
        System.out.println(single1.equals(single2));
    }

}

该单例模式的思路是通过将自身的实例对象设置为一个属性,并加上static以及final修饰符,去保证在一个jvm中只有一个这样的实例,然后将getInstance()方法标注为静态来实现以实现外部调用该实例。

单例模式主要有如下两个优势:

  • 减少创建Java实例所带来的系统开销
  • 便于系统跟踪单个Java实例的生命周期、实例状态等

简单工厂模式

简单工厂模式是由一个工厂对象决定其创建出哪一种产品的实例。

举个简单的例子来说:

A实例调用B实例的方法,称为A依赖B。如果使用new关键字来创建一个B实例,然后调用B实例的方法。那么当系统需要重构的时候,比如使用C类来代替B类,那么不得不更改A类的代码。如果用工厂模式则不需要关心B对象的实现以及创建过程。

工厂模式的伪代码如下:

首先创建一个Product接口,这个接口我们把它比作是工厂中的产品工厂部门

/**
 * Created by lany on 2019/4/9.
 */
public interface Product {

    String productName();

}

创建Apple类,用于实现Product接口,这个‘苹果部门’是专门用来生产苹果的。

/**
 * Created by lany on 2019/4/9.
 */
public class Apple implements Product {

    @Override
    public String productName() {
        return "Apple";
    }
}

创建Banana类,用于实现Product接口,这个‘香蕉部门’是专门用来生产香蕉的。

/**
 * Created by lany on 2019/4/9.
 */
public class Banana implements Product {

    @Override
    public String productName() {
        return "Banana";
    }
}

创建Factory类,这个类我们将它比作工厂

/**
 * Created by lany on 2018/9/3.
 */
public class Factory {

    private String productName;

    public Factory(String productName) {
        this.productName = productName;
    }

    Product getProduct(){
        if(this.productName.equalsIgnoreCase("apple")){
            return new Apple();
        }else
            return new Banana();
    }

    public static void main(String[] args){
        Factory factory=new Factory("Apple");
        System.out.println(factory.getProduct().productName());
        /*Assert.assertEquals("Apple",productFactory.getProduct().productName());*/
    }

}

}

上面的伪代码主要思路是通过接口将对象的调用和对象的创建过程分离,当对象调用者需要对象时,直接向工厂请求即可。这样从而避免的对象的调用者与对象的实现类以硬编码的方式耦合,以此提高系统的可维护性,可扩展性。

简单工厂模式也有一个缺陷:那就是当产品工厂部门作了修改之后,产品工厂也随之需要修改。

工厂模式和抽象工厂

如果不想在上个设计模式的工厂类中进行逻辑判断,可以为不同的产品提供不同的工厂。

伪代码如下:

创建 ProductFactory接口,该接口可以比作抽象出来的产品工厂。

/**
 * Created by lany on 2019/4/9.
 */
public interface ProductFactory {

    Product getProduct();

}

创建AppleFactory类,该类为产品工厂的具体实现,只生产苹果。

/**
 * Created by lany on 2019/4/9.
 */
public class AppleFactory implements ProductFactory {

    @Override
    public Product getProduct() {
        return new Apple();
    }
}

创建BananaFactory类,该类为产品工厂的具体实现,只生产香蕉。

/**
 * Created by lany on 2019/4/9.
 */
public class BananaFactory implements ProductFactory {

    @Override
    public Product getProduct() {
        return new Banana();
    }
}

获取产品

    public static void main(String[] args){
        ProductFactory productFactory = new AppleFactory();
        System.out.println(productFactory.getProduct().productName());
    }

在使用工厂模式需要注意的是,对象调用者需要与具体的工厂类耦合

代理模式

代理模式是一种应用非常广泛的设计模式,当客户端代码需要调用某个对象时,客户端实际上不关心是否准确得到该对象,它只要一个能提供该功能的对象即可,此时我们就可返回该对象的代理(Proxy)。

简单点说,代理就是一个Java对象代表另一个Java对象来采取行动。

静态代理

伪代码如下:

创建Image接口

public interface Image {
    void show();
}

创建BigImage类,该类为Image接口的具体实现类

/**
 * Created by lany on 2018/9/11.
 */
public class BigImage implements Image {

    public BigImage() {
        System.out.println("new BigImage");
    }

    @Override
    public void show() {
        System.out.println("This is BigImage");
    }
}

创建ImageProxy类,该类作为代理对象

/**
 * 代理对象
 * Created by lany on 2018/9/11.
 */
public class ImageProxy implements Image{

    /**
     * 创建一个实例,作为被代理对象
     */
    private Image image;
    
    /**
     * 使用抽象实体来实例话代理对象
     * @param image
     */
    public ImageProxy(Image image){
        this.image = image;
    }
    
    /**
     * 重写Image接口的show()方法
     * 该方法用于控制对被代理对象的访问
     * 并根据需要创建和删除被代理对象
     */
    @Override
    public void show() {
        if(image == null){
            image = new BigImage();
        }
        image.show();
    }
}

调用的时候,先不创建

Image image = new ImageProxy(null);

只有当真正调用image实例的show()方法时才创建对象。

image.show();

代理模式在Hibernate的懒加载机制中也有用到。当系统加载A实体时,与A实体相关联的B实体并未被加载出来,A实体所关联的B实体全部都是代理对象,只有等到A实体真正需要去访问B实体时,系统才会在数据库中抓取B实体所对应的记录。

动态代理

借助于Java提供的Proxy和InvocationHandler,可以实现在运行时生成动态代理的功能,而动态代理对象就可以作为目标对象使用,而且增强了目标对象的功能。

伪代码如下:

创建Panther接口

/**
 * Created by lany on 2018/9/11.
 */
public interface Panther {

    void info();

    void run();

}

创建GunPanther类,该类为Panther接口的具体实现类

/**
 * Created by lany on 2018/9/11.
 */
public class GunPanther implements Panther{

    @Override
    public void info() {
        System.out.println("我是一只猎豹");
    }

    @Override
    public void run() {
        System.out.println("我奔跑迅速");
    }
}

创建MyInvocationHandler,该类增强了代理对象的功能

/**
 * Created by lany on 2018/9/11.
 */
public class MyInvocationHandler implements InvocationHandler {

    private Object target;

    public void setTarget(Object target){
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Exception {
        Object result = method.invoke(target,args);
        System.out.println("我被增强了,我跑的超快");
        return result;
    }
}

创建MyProxyFactory类,该类为代理对象

/**
 * Created by lany on 2018/9/11.
 */
public class MyProxyFactory {

    public static Object getProxy(Object target) throws Exception{

        MyInvocationHandler handler = new MyInvocationHandler();

        handler.setTarget(target);

        return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),handler);

    }
}

同时调用没有代理的对象,以及代理对象的方法

public class Test {

    public static void main(String[] args){
        try {
            Panther target = new GunPanther();
            target.info();

            System.out.println("=========");
            Panther panther = (Panther)MyProxyFactory.getProxy(target);
            panther.info();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

可以看出没有通过代理的对象,和通过代理的对象在调用之后的结果不一样

我是一只猎豹
=========
我被增强了,我跑的超快
我是一只猎豹

这种动态代理方式在Spring的AOP中也得到了实现,但是Spring AOP更灵活。