JAVA中的几种设计模式

单例模式

饿汉模式

Singleton.java

package com.imooc
public class Singleton {
    // 1.将构造方法私有化,不允许外部直接创建对象
    private Singleton() {
        
    }
    // 2.创建类的唯一示例,使用private static修饰
    private static Singleton instance = new Singleton();
    // 3.提供一个用于获取示例的方法
    public static Singleton getInstance() {
        return instance;
    }
}

饿汉模式指第二步时,加载类的同时生成了实例化的对象。特点是加载类时比较慢,但运行时获取对象的速度比较快,线程安全。

Test.java

package com.imooc;
public class Test {
    private static void main(String[] args) {
        Singleton s1 = Singleton.getInstance;
        Singleton s2 = Singleton.getInstance;
    }
}

懒汉模式

Singleton2.java

package com.imooc

public class Singleton2 {
    // 1.将构造方法私有化,不允许外部直接创建对象
    private Singleton2() {
        
    }

    // 2.创建类的唯一示例,使用private static修饰
    private static Singleton2 instance;

    // 3.提供一个用于获取示例的方法
    public static Singleton2 getInstance() {
        if(install==null){
            instance = new Singleton2();
        }
        return instance;
    }
}

懒汉模式指第二步没有实例化对象,而是在第三步,当调用方法,才会判断并进行实例化,特点是加载类时比较快,但运行时获取对象的速度比较慢,线程不安全。

模板方法模式

应用场景为模拟制备饮料。制备饮料一共有四个步骤,即为RefreshBeverage.java中注释中写到的四步。泡制饮料和加入调味料在抽象基类中没有提供具体的实现方法,只是定义接口方法,在子类中应用模板方法模式分别实现具体内容。

第四步加入调味料用到了钩子方法,可以提高代码灵活性,判断子类要泡制的饮料中要不要加入调味料。

RefreshBeverage.java

package com.imooc.pattern.template;

/**
 * 抽象基类,为所有子类提供一个算法框架
 * 
 * 提神饮料
 */
public abstract class RefreshBeverage {
    
    /**
     * 制备饮料的模板方法
     * 封装了所有子类共同遵循的算法框架
     */
    public final void prepareBeverageTemplate() {
        // 步骤1 将水煮沸
        boilWater();
        // 步骤2 泡制饮料
        brew();
        // 步骤3 将饮料倒入杯中
        pourInCup();
        if(isCustomerWantsCondiments()){
            // 步骤4 加入调味料
            addCondiments();
        }
    }

    /**
     * Hook,钩子函数,提供一个默认或空的实现
     * 具体的子类可以自行决定是否挂钩或者如何挂钩
     * 询问用户是否加入调料
     */
    protected boolean isCustomerWantsCondiments() {
        return true;
    }

    /**
     * 基本方法,将水煮沸
     */
    private void boilWater() {
        System.out.println("将水煮沸");
    }

    /**
     * 抽象的基本方法,泡制饮料
     */
    protected abstract void brew();

    /**
     * 基本方法,将饮料倒入杯中
     */
    private void pourInCup() {
        System.out.println("将饮料倒入杯中");
    }

    /**
     * 抽象的基本方法,加入调味料
     */
    protected abstract void addCondiments();
}

Coffee.java

package com.imooc.pattern.template;

/**
 * 具体子类,提供了咖啡制备的具体实现
 */
public class Coffee extends RefreshBeverage {
    
    protected void brew() {
        System.out.println("用沸水冲泡咖啡");
    }

    protected void addCondiments() {
        System.out.println("加入糖和牛奶");
    }
}

Tea.java

package com.imooc.pattern.template;

/**
 * 具体子类,提供了制备茶的具体实现
 */
public class Tea extends RefreshBeverage {

    protected void brew() {
        System.out.println("用80度的热水浸泡茶叶5分钟");
    }

    protected void addCondiments() {
        System.out.println("加入柠檬");
    }
    
    /**
     * 子类通过覆盖的方式选择挂载钩子函数
     */
    protected boolean isCustomerWantsCondiments(){
        return false;
    }

}

RefreshBeverageTest.java

package com.imooc.pattern.template;

public class RefreshBeverageTest {

    public static void main(String[] args) {

        System.out.println("制备咖啡...");
        RefreshBeverage b1 = new Coffee();
        b1.prepareBeverageTemplate();
        System.out.println("咖啡好了!");
        
        System.out.println("**********************************");
        
        System.out.println("制备茶...");
        RefreshBeverage b2 = new Tea();
        b2.prepareBeverageTemplate();
        System.out.println("茶好了!");
    }

}

运行结果:

制备咖啡...
将水煮沸
用沸水冲泡咖啡
将饮料倒入杯中
加入糖和牛奶
咖啡好了!
**********************************
制备茶...
将水煮沸
用80度的热水浸泡茶叶5分钟
将饮料倒入杯中
茶好了!

其中用到的钩子方法会使子类更加灵活。

适配器模式

应用场景为模拟二相转三相插座适配器,NoteBook需要三相电流供电,但只有二相电流插座,TwoPlugAdapter.java中适配了二相电流。

ThreePlugIf.java

package com.imooc.apttern.adapter;
/**
 * 三相插座接口
 */
public interface ThreePlugIf {
    
    // 使用三相电流供电
    public void powerWithThree(){
        
    }
}

GBTwoPlug.java

package com.imooc.apttern.adapter;

public class GBTwoPlug {

    // 使用二相电流供电
    public void powerWithTwo(){
        System.out.println("使用二相电流供电");
    }
}

NoteBook.java

package com.imooc.apttern.adapter;

public class NoteBook {

    private ThreePlugIf plug;
    
    public NoteBook(ThreePlugIf plug){
        this.plug = plug;
    }
    
    // 使用插座充电
    public void change(){
        plug.powerWithThree();
    }
    
    public static void main(String[] args){
        GBTwoPlug two = new GBTwoPlug();
        ThreePlugIf three = new TwoPlugAdapter(two);
        NoteBook nb = new NoteBook(three);
        nb.change();
    }
}

TwoPlugAdapter.java

package com.imooc.apttern.adapter;

/**
 * 二相转三相的插座适配器
 */
public class TwoPlugAdapter implements ThreePlugIf {

    private GBTwoPlug plug;
    
    public TwoPlugAdapter(GBTwoPlug plug){
        this.plug = plug;
    }

    public void powerWithThree() {
        System.out.println("通过转化");
        plug.powerWithTwo();
    }

}

运行结果:

通过转化
使用二相电流供电

策略模式

策略模式将可变部分从程序中抽象分离成算法接口,在该接口下分别封装一系列算法实现

What is composition ?

在类中增加一个私有域,引用另一个已有的类的实例,通过调用引用实例的方法从而获得新的功能,这种设计被称作组合(复合)。

策略模式的优点:

1.使用了组合,是架构更加灵活

2.富有弹性,可以较好的应对变化(开——闭原则)

3.更好的代码复用性(相对于继承)

4.消除大量的条件语句

策略模式的缺点:

1.客户代码需要了解每个策略的细节

2.增加了对象的数目

策略模式的应用场景:

1.许多相关的类仅仅是行为差异

2.运行时选取不同的算法变体

3.通过条件语句在多个分支中选取一

代码的应用场景模拟了鸭子的生产过程,鸭子一共有三个行为:展示、鸣叫、飞行。不同的鸭子有不同的飞行行为,使用到了策略模式。

Duck.java

package com.imooc.pattern.strategy;

/**
 * 超类,所有的鸭子都要继承此类
 *抽象了鸭子的行为:显示和鸣叫
 */
public abstract class Duck {

    /**
     * 鸭子发出叫声
     * 通用行为,由超类实现
     */
    public void quack(){
        System.out.println("嘎嘎嘎");
    }
    
    /**
     * 显示鸭子的外观
     * 鸭子的外观不相同,声明为abstract,由子类实现
     */
    public abstract void display();
    
    private FlyingStrategy flyingStrategy;
    
    public void setFlyingStrategy(FlyingStrategy flyingStrategy){
        this.flyingStrategy = flyingStrategy;
    }
    
    public void fly(){
        flyingStrategy.performFly();
    }
}

FlyingStrategy.java

package com.imooc.pattern.strategy;

/**
 * 策略接口,实现鸭子的飞行行为
 */
public interface FlyingStrategy {
    void performFly();
}

MallardDuck.java

package com.imooc.pattern.strategy;

import com.imooc.pattern.strategy.impl.FlyWithWin;

// 绿脖鸭
public class MallardDuck extends Duck {

    public MallardDuck(){
        super();
        super.setFlyingStrategy(new FlyWithWin());
    }
    
    @Override
    public void display() {
        // TODO Auto-generated method stub
        System.out.println("我的脖子是绿色的");
    }

}

RedheadDuck.java

package com.imooc.pattern.strategy;

import com.imooc.pattern.strategy.impl.FlyWithWin;

// 红头鸭
public class RedheadDuck extends Duck {

    public RedheadDuck(){
        super();
        super.setFlyingStrategy(new FlyWithWin());
    }
    
    public void display() {
        System.out.println("我的头是红色的");
    }

}

RubberDuck.java

package com.imooc.pattern.strategy;

import com.imooc.pattern.strategy.impl.FlyNoWay;

// 橡胶鸭
public class RubberDuck extends Duck {

    public RubberDuck(){
        super();
        super.setFlyingStrategy(new FlyNoWay());
    }
    
    @Override
    public void display() {
        // TODO Auto-generated method stub
        System.out.println("我全身发黄,嘴巴很红");
    }

    public void quack(){
        // 叫声稍有不同,复写了父类方法
        System.out.println("嘎~嘎~嘎~");
    }
}

不同的飞行策略类:

FlyWithWin.java

package com.imooc.pattern.strategy.impl;

import com.imooc.pattern.strategy.FlyingStrategy;

public class FlyWithWin implements FlyingStrategy {

    @Override
    public void performFly() {
        // TODO Auto-generated method stub
        System.out.println("振翅高飞");
    }

}

FlyNoWay.java

package com.imooc.pattern.strategy.impl;

import com.imooc.pattern.strategy.FlyingStrategy;

public class FlyNoWay implements FlyingStrategy {

    @Override
    public void performFly() {
        // TODO Auto-generated method stub
        System.out.println("我不会飞行!");
    }

}

最后测试生产出的鸭子:

DuckTest.java

package com.imooc.pattern.strategy;

public class DuckTest {

    public static void main(String[] args){
        System.out.println("测试鸭子程序");
        Duck duck = null;
//      duck = new MallardDuck();
//      duck = new RedheadDuck();
        duck = new RubberDuck();
        
        duck.display();
        duck.quack();
        duck.fly();
        
        System.out.println("测试完毕");
    }
}

运行结果:

测试鸭子程序
我全身发黄,嘴巴很红
嘎~嘎~嘎~
我不会飞行!
测试完毕

这样就用JAVA实现了策略模式。

代理模式

远程代理:为不同地理的对象提供局域网代表对象

虚拟代理:根据需要将资源消耗很大的对象进行延迟,真正需要的时候进行创建

保护代理:控制用户的访问权限

智能引用代理:提供对目标对象额外服务

应用场景模拟了汽车行驶时间的显示和日志记录两个功能。

Moveable.java

package com.imooc.proxy;

// 接口文件
public interface Moveable {
    void move();
}

Car.java

package com.imooc.proxy;

import java.util.Random;

public class Car implements Moveable {

    public void move() {
        
        // 实现开车
        try{
            Thread.sleep(new Random().nextInt(1000));
            System.out.println("汽车行驶中……");
        }catch(InterruptedException e){
            e.printStackTrace();
        }
    }
    
}

Car2.java

package com.imooc.proxy;

// 实现汽车行驶时间
public class Car2 extends Car {

    public void move(){
        long strattime = System.currentTimeMillis();
        System.out.println("汽车开始行驶……");
        super.move();
        long endtime = System.currentTimeMillis();
        System.out.println("汽车结束行驶中…… 汽车行驶时间:"+(endtime-strattime)+"毫秒!");
    }
}

Car3.java

package com.imooc.proxy;

// 时间代理实现汽车行驶时间
public class Car3 implements Moveable {
    
    public Car3(Car car) {
        super();
        this.car = car;
    }

    private Car car;

    public void move(){
        long strattime = System.currentTimeMillis();
        System.out.println("汽车开始行驶……");
        car.move();
        long endtime = System.currentTimeMillis();
        System.out.println("汽车结束行驶中…… 汽车行驶时间:"+(endtime-strattime)+"毫秒!");
    }

}

Car4.java

package com.imooc.proxy;

// 增加log记录代理
public class Car4 implements Moveable {

    public Car4(Moveable m) {
        super();
        this.m = m;
    }

    private Moveable m;

    public void move(){
        System.out.println("日志开始……");
        m.move();
        System.out.println("日志结束……");
    }

}

Client.java

package com.imooc.proxy;

/**
 * 测试类
 */
public class Client {

    public static void main(String[] args){
        
        // 直接
//      Car car = new Car();
//      car.move();
        
        // 使用集成方法
//      Moveable m = new Car2();
//      m.move();
        
        // 使用聚合方法
//      Car car = new Car();
//      Moveable m = new Car3(car);
//      m.move();
        
        // 增加日志记录
        Car car = new Car();
        Car3 ctp = new Car3(car);
        Car4 clp = new Car4(ctp);
        clp.move();
    }
}

运行结果像这样:

日志开始……
汽车开始行驶……
汽车行驶中……
汽车结束行驶中…… 汽车行驶时间:497毫秒!
日志结束……

下面使用动态代理来是代码更灵活(有错误)。

TimeHandler.java

package com.imooc.jdkproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

import com.imooc.proxy.Car;

/**
 * JDK动态代理
 * 只能代理实现了接口的类
 * 没有实现接口的类不能实现JDK的动态代理
 */
public class TimeHandler implements InvocationHandler {

    public TimeHandler(Object target){
        super();
        this.target = target;
    }
    
    private Object target;
    
    /**
     * 参数
     * proxy 被代理对象
     * method 被代理的方法
     * args 方法的参数
     * 
     * 返回值
     * Object 方法的返回值
     */
    @Override
    public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable {
        // TODO Auto-generated method stub
        
        long strattime = System.currentTimeMillis();
        System.out.println("汽车开始行驶……");
        method.invoke(target);
        long endtime = System.currentTimeMillis();
        System.out.println("汽车结束行驶中…… 汽车行驶时间:"+(endtime-strattime)+"毫秒!");

        return null;
    }

}

Test.java

package com.imooc.jdkproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

import com.imooc.proxy.Car;
import com.imooc.proxy.Moveable;

/**
 * JDK动态代理测试类
 */
public class Test {

    public static void main(String[] args){
        Car car = new Car();
        InvocationHandler h = new TimeHandler(car);
        Class<?> cls = car.getClass();
        
        /**
         * loader 类加载器
         * interface 实现节后
         * h InvocationHander
         */
        Moveable m = (Moveable)Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), h);
        m.move();
    }
}

etc.