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.