2011年12月31日土曜日

[设计模式] 状态模式 (Stage)

1. 定义
允许一个对象在其内部状态改变时改变它的行为, 这个对象看起来似乎修改了它的类
有效的替换充满在程序中的 ifelse 语句, 将不同条件下的行为封装在一个类里面, 再给这些类一个统一的父类来约束他们

2. 场景
2-1. 一个对象的行为取决于它的状态, 并且他必须在运行时刻根据状态改变它的行为
2-2. 一个操作中含有庞大的多分枝的条件语句, 并且这些分支依赖于该对象的状态, 这个状态通常用一个或多个枚举常量表示. 通常, 有多个操作包含这一相同的条件结构. 状态模式将每一个条件分支放入一个独立的类中, 这是得你可以根据对象自身的情况将对象的状态作为一个对象, 这一对象可以不依赖于其他对象而独立变化

2. 角色
2-1. 使用环境角色: 客户端需要的接口, 维护一个具体状态角色的实例, 这个实例来决定当前的状态
2-2. 抽象状态角色: 各个状态的接口或抽象类
2-3. 具体状态角色: 状态角色具体类

3-3. 实例
// 抽象状态角色
interface State {
    public void shot();
}

// 非正常状态
public class NonormalState implements State {
    public void shot(){
        System.out.println("今天你投篮十中一");
    }
}

// 正常状态
public class NormalState implements State {
    public void shot(){
        System.out.println("今天你投篮十中五");
    }
}

// 超常状态
public class SuperState implements State {
    public void shot(){
        System.out.println("今天你投篮十中十");
    }
}

// 使用环境角色
public class Player {
private State state = new NormalState(); // 默认正常状态
    public void setState(State state) {
        this.state = state;
    }
    public void shot() {
        state.shot();
    }
}

//
public class StateTest
{
    public static void main(String[] args) {
        Player player = new Player();
        player.shot(); // 正常下投篮
        player.setState(new NonormalState());
        player.shot(); // 不正常下投篮
        player.setState(new SuperState());
        player.shot(); // 超常下投篮
    }
}

[设计模式] 策略模式 (Strategy)

1. 场景
算法簇模式, 就是定义了不同的算法族, 并且之间可以互相替换, 此模式让算法的变化独立于使用算法的客户
行为模式之一, 对一系列的算法加以封装, 为所有算法定义一个抽象的算法接口, 并通过继承该抽象算法接口对所有的算法加以封装和实现
具体的算法选择交由客户端决定 (策略)
Strategy 模式主要用来平滑地处理算法的切换

2. 角色
2-1. 环境对象: 策略的外部封装类, 或者说策略的容器类, 该类中实现了对抽象策略中定义的接口或者抽象类的引用
2-2. 抽象策略对象: 接口或抽象类
2-3. 具体策略对象: 封装了实现同不功能的不同算法

3-3. 实例
// 抽象策略对象
public interface Strategy {
    public double cost(double num);
}

// 具体策略对象 A
public class StrategyA implements Strategy{
    public double cost(double num) {
        return num * 0.8;
    }
}

// 具体策略对象 B
public class StrategyB implements Strategy {
    public double cost(double num) {
        if(num >= 200) {
            return num - 50;
        }
        return num;
    }
}

// 环境对象
public class Context {
    private Strategy strategy;
    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    public double cost(double num){
        return this.strategy.cost(num);
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        double num = 200;
        Context context = new Context(new StrategyB());
        double newNum = context.cost(num);
        System.out.println("实际付账" + newNum + "元");
    }
}

2011年12月30日金曜日

[设计模式] 装饰模式 (Decorator)

1. 场景
需要为已经设计好的类添加新的职责
又称包装 (Wrapper) 模式, 是以对客户端透明的方式扩展对象的功能
是继承关系的一个替代方案, 对客户端透明意味着接口不变

2. 角色
2-1. 抽象构件: 原始的功能接口
2-2. 具体构件: 原始的具体实现类
2-3. 装饰角色: 持有具体构件类的对象, 以便执行原有功能
2-4. 具体装饰: 具体扩展的功能实现类

3-3. 实例
// 原始的功能接口
public interface Component {
    void operation();
}

// 原始的具体实现类
public class ConcreteComponent implements Component {
    public void operation() {
        System.out.println("I'm " + this.getClass().getName());
    }
}

// 装饰角色
public class Decorator implements Component {
    private Component component;

    public Decorator(Component component) {
        this.component = component;
    }

    public void operation() {
        component.operation();
    }
}

// 具体装饰
public class BeforeDecorator extends Decorator {
    public BeforeDecorator(Component component) {
        super(component);
    }

    public void operation() {
        before();
        super.operation();
    }

    private void before() {
        System.out.println("before: I'm "+this.getClass().getName());
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        Component component = new ConcreteComponent();
        component.operation(); // 原来调用方式
        System.out.println("----------------------------------------");
        component = new BeforeDecorator(new ConcreteComponent());
        component.operation(); // 装饰后的调用方式, 对客户端接口不变
    }
}

[设计模式] 代理模式 (Proxy)

1. 场景
给某一对象提供代理对象, 并由代理对象控制此具体对象的访问
在某些情况下, 一个客户不想或者不能直接引用另一个对象, 而代理对象可以在客户端和目标对象之间起到中介的作用
例如授权机制, 通过代理能拦截真实主题的能力, 来控制真实主题的访问权限

2. 角色
2-1. 抽象角色: 声明真实对象和代理对象的共同接口
2-2. 代理角色: 代理对象角色内部含有对真实对象的引用, 从而可以操作真实对象, 同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象. 同时, 代理对象可以在执行真实对象操作时, 附加其他的操作, 相当于对真实对象进行封装
2-3. 真实角色: 代理角色所代表的真实对象, 是最终要引用的对象

3. 种类
3-1. 静态代理
3-2. 动态代理

3-3. 实例
3-3-1. 静态代理
// 抽象角色 (抽象类或接口)
public abstract class Subject {
    abstract public void request();
}

// 真实角色
public class RealSubject extends Subject {
    public void request() {
        System.out.println("真实角色的请求方法 RealSubject.request() 被调用!");
    }
}

// 代理角色
public class ProxySubject extends Subject {
    private RealSubject realSubject; // 以真实角色作为代理角色的属性

    // 该方法封装了真实对象的 request 方法
    public void request() {
        preRequest(); //代理前执行的操作
        if (realSubject == null) {
            realSubject = new RealSubject();
        }
        realSubject.request(); // 此处执行真实对象的request方法
        postRequest(); //代理后执行的操作
    }

    private void preRequest() {
        System.out.println("代理前执行的操作 ProxySubject.preRequest()!");
    }

    private void postRequest() {
        System.out.println("代理后执行的操作 ProxySubject.postRequest()!");
    }
}

// 客户端
public class Test {
    public static void main(String[] args) {
        Subject sub = new ProxySubject();
        sub.request();
    }
}

3-3-2. 动态代理

// 抽象角色
public interface Subject {
    public void request();
}

// 真实角色
public class RealSubject implements Subject {
    public void request() {
        System.out.println("具体角色的请求方法RealSubject.request()被调用!");
    }
}

/**
 * 代理处理器
 *
 * 该代理类的内部属性为 Object 类, 实际使用时通过该类的构造函数 DynamicSubject(Object obj) 对其赋值
 * 在该类还实现了 invoke 方法, 该方法中的 method.invoke(sub,args), 其实就是调用被代理对象的将要被执行的方法
 * 方法参数 sub 是实际的被代理对象, args 为执行被代理对象相应操作所需的参数
 */

public class DynamicSubject implements InvocationHandler {
    private Object sub; //被代理对象

    /**
     * 构造方法, 并初始化被代理对象
     * @param obj 是被代理的对象
     */
    public DynamicSubject(Object obj) {
        sub = obj;
    }

    /**
     * @param proxy  指代理类
     * @param method 被代理的方法
     * @param args   被代理的方法所需要的参数数组
     * @return  在代理实例上处理方法调用并返回结果。
     * @throws Throwable
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理前执行的操作!");
        method.invoke(sub, args);
        System.out.println("代理后执行的操作!");
        return null;
    }
}

// 客户端
public class Test {
    static public void main(String[] args) throws Throwable {
        RealSubject rs = new RealSubject(); // 在这里指定被代理类

        //通过被代理类对象声明一个代理类对象
        InvocationHandler dynamicSubject = new DynamicSubject(rs);
        Class<?> cls = rs.getClass();

        // 生成代理类实例
        // 返回一个指定接口的代理类实例
        // 该接口可以将方法调用指派到指定的调用处理程序
        Subject subject = (Subject) Proxy.newProxyInstance(
                cls.getClassLoader(), //定义代理类的类加载器
                cls.getInterfaces(), //代理类要实现的接口列表
                dynamicSubject); //指派方法调用的调用处理程序: InvocationHandler 对象

        subject.request(); //调用代理处理器上的方法
    }
}

3-3-3. 动态代理的实现 AOP

// 切面接口, 通过实现这个接口可以对指定函数在调用前后进行处理
public interface AopInterface {
    public void before(Object obj); //调用的处理
    public void end(Object obj); //调用后的处理
}

// 切面接口实现类
public class AopInterfaceImp implements AopInterface{ 
    public void before(Object obj) {
        System.out.println("调用前拦截");
    }

    public void end(Object obj) {
        System.out.println("调用调用后处理");
    }
}

// 代理类
public class PeoxyObject implements InvocationHandler {
    private AopInterface aop; //定义了切入时调用的方法
    private Object proxy_obj;
    private String methodName; //指定要切入的方法名

    public Object factory(Object obj){
        proxy_obj = obj;
        Class cls = obj.getClass();
        return Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), this);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if(this.aop == null)throw new NullPointerException("aop is null");
        if(method == null)throw new NullPointerException("method is null");

        Object o;

        // 如果指定了要拦截方法名, 并且调用的方法和指定的方法名相同, 则进行拦截处理
        // 否则当正常方法处理
        if(methodName != null && method.toString().indexOf(methodName) != -1){
            aop.before(proxy_obj); //指定方法调用前的处理
            o = method.invoke(proxy_obj, args); // 调用真实方法
            aop.end(proxy_obj);//指定方法调用后的处理
        } else {
            //没有指定的方法, 以正常方法调用
            o = method.invoke(proxy_obj, args);
        }

        return o;
    }

    public AopInterface getAop() {
        return aop;
    }

    public void setAop(AopInterface aop) {
        this.aop = aop;
    }

    public String getMethodName() {
        return methodName;
    }

    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }
}

// 抽象角色
public interface SubInterface {
    public void add(String value1,String value2);
    public void acc(String value1);
}

// 真实角色
public class ImpObject implements SubInterface{
    public void add(String value1,String value2) {
        System.out.println("ImpObject add(String value1,String value2)");
    }

    public void acc(String value1){
        System.out.println("ImpObject acc(String value1)");
    }
}

// 测试类
public static void main(String agr[]){
    PeoxyObject po = new PeoxyObject();
    po.setAop(new AopInterfaceImp()); // 拦截处理对象
    po.setMethodName("acc");// 指定要拦截的函数
    SubInterface si = (SubInterface)po.factory(new ImpObject());

    // 因为 add 方法不是我们指定的拦截函数, AopInterfaceImp 是不会被执行
    si.add("tt","dd");

    // acc 是我们指定的拦截方法, 所以调用 acc 的前后会先执行 AopInterfaceImp
    si.acc("tt");
}

2011年12月25日日曜日

[设计模式] 观察者模式 (Observer)

1. 场景
观察者模式定义了对象间的一对多依赖关系, 让一个或多个观察者对象观察一个主题对象
当主题对象的状态发生变化时, 系统能通知所有的依赖于此对象的观察者对象, 从而使得观察者对象能够自动更新
实际上, 完全可以把观察者接口理解成事件监听接口, 而被观察者对象也可当成事件源来处理, Java 事件机制的底层实现, 本身就是通过观察者模式来实现的

2. 角色
Subject: 主题
Observer: 观察者

3. 实例

3-1. 主题抽象类
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public abstract class Observable {
    // 用一个 List 来保存该对象上所有绑定的事件监听器
    List<Observer> observers = new ArrayList<Observer>();

    // 定义一个方法, 用于从该主题上注册观察者
    public void registObserver(Observer o) {
        observers.add(o);
    }

    // 定义一个方法,用于从该主题中删除观察者
    public void removeObserver(Observer o) {
        observers.add(o);
    }

    // 通知该主题上注册的所有观察者
    public void notifyObservers(Object value) {
        // 遍历注册到该被观察者上的所有观察者
        for (Iterator<Observer> it = observers.iterator(); it.hasNext();) {
            Observer o = it.next();
            // 显示每个观察者的update方法
            o.update(this, value);
        }
    }
}

3-2. 主题类
public class Product extends Observable {
    private String name;
    private double price;
    public Product(String name, double price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    // 当程序调用 name 的 setter 方法来修改 Product 的 name 属性时
    // 程序自然触发该对象上注册的所有观察者
    public void setName(String name) {
        this.name = name;
        notifyObservers(name);
    }

    public double getPrice() {
        return price;
    }

    // 当程序调用 price 的 setter 方法来修改 Product 的 price 属性时
    // 程序自然触发该对象上注册的所有观察者
    public void setPrice(double price) {
        this.price = price;
        notifyObservers(price);
    }
}

3.3 观察者接口
public interface Observer {
    void update(Observable o, Object arg);
}

3-4. 观察者 (1)
public class NameObserver implements Observer {
    // 实现观察者必须实现的 update 方法
    public void update(Observable o, Object arg) {
        if (arg instanceof String) {
            String name = String.valueOf(arg);
            System.out.println("名称观察者:" + o + "物品名称已经改变为: " + name);
        }
    }
}

3-5. 观察者 (2)
public class PriceObserver implements Observer {
    // 实现观察者必须实现的 update 方法
    public void update(Observable o, Object arg) {
        if (arg instanceof Double) {
            System.out.println("价格观察者:" + o + "物品价格已经改变为: " + arg);
        }
    }
}

3-6. 测试类
public class Test {
    public static void main(String[] args) {
        // 创建一个被观察者对象
        Product p = new Product("电视机", 176);
        // 创建两个观察者对象
        NameObserver no = new NameObserver();
        PriceObserver po = new PriceObserver();
        // 向被观察对象上注册两个观察者对象
        p.registObserver(no);
        p.registObserver(po);
        // 程序调用 setter 方法来改变 Product 的 name 和 price 属性
        p.setName("书桌");
        p.setPrice(345f);
    }
}

[设计模式] 桥接模式 (Bridge)

1. 场景
结构型模式
应对某个类具有两个或两个以上的维度变化
把变化部分抽象出来, 使变化部分与主类分离开来, 从而将多个维度的变化彻底分离
最后提供一个管理类来组合不同维度上的变化, 通过这种组合来满足业务的需要
JavaEE应用中常见的 Service 层对 DAO 层的引用正是桥接模式的应用

2. 实例
AbstractNoodle (面抽象类)
--BeefMoodle (牛肉面)
--PorkyNoodle (猪肉面)

Peppery (胡椒抽象接口)
--PepperySytle (加胡椒)
--PlainStyle (不加胡椒)

2-1. 抽象类
public abstract class AbstractNoodle {
    // 组合一个 Peppery 变量, 用于将该维度的变化独立出来
    protected Peppery style;

    // 每份 Noodle 必须组合一个 Peppery 对象
    public AbstractNoodle(Peppery style) {
        this.style = style;
    }

    public abstract void eat();
}

2-2. 子类 (1)
public class BeefMoodle extends AbstractNoodle {
    public BeefMoodle(Peppery style) {
        super(style);
    }

    // 实现 eat() 抽象方法
    public void eat() {
        System.out.println("这是一碗美味的牛肉面条. " + super.style.style());
    }
}

2-3. 子类 (2)
public class PorkyNoodle extends AbstractNoodle {
    public PorkyNoodle(Peppery style) {
        super(style);
    }

    // 实现 eat() 抽象方法
    public void eat() {
        System.out.println("这是一碗稍嫌油腻的猪肉面条. " + super.style.style());
    }
}

2-4. 行为/属性抽象 (接口)
public interface Peppery {
    String style();
}

2-5. 行为/属性实现类 (1)
public class PepperySytle implements Peppery {
    // 实现 "辣味" 风格的方法
    public String style() {
        return "辣味很重, 很过瘾...";
    }
}

2-6. 行为/属性实现类 (2)
public class PlainStyle implements Peppery {
    // 实现 "不辣" 风格的方法
    public String style() {
        return "味道清淡, 很养胃...";
    }
}

// 测试类
public class Test {
    public static void main(String[] args) {
        // 下面将得到“辣味”的牛肉面
        AbstractNoodle noodle1 = new BeefMoodle(new PepperySytle());
        noodle1.eat();
        // 下面将得到“不辣”的牛肉面
        AbstractNoodle noodle2 = new BeefMoodle(new PlainStyle());
        noodle2.eat();
        // 下面将得到“辣味”的猪肉面
        AbstractNoodle noodle3 = new PorkyNoodle(new PepperySytle());
        noodle3.eat();
        // 下面将得到“不辣”的猪肉面
        AbstractNoodle noodle4 = new PorkyNoodle(new PlainStyle());
        noodle4.eat();
    }
}

2011年12月24日土曜日

[设计模式] 门面模式 (Facade)

门面模式 Facade 也被称为正面模式, 外观模式, 这种模式用于将一组复杂的类包装到一个简单的外部接口中

1. Facade 模式可以应用于
1-1. 不需要使用一个复杂系统的所有功能, 而且可以创建一个新的类, 包含访问系统的所用规则. 如果只需要使用系统的部分功能, 那么为新类创建的 API 将比原有系统的 API 简单的多
1-2. 希望封装或者隐藏原系统
1-3. 希望使用原系统的功能, 而且还希望增加一些附件功能

2. 角色
2-1. 门面角色
这是门面模式的核心
它被客户角色调用, 因此它熟悉子系统的功能, 它内部根据客户角色已有的需求预定了几种功能组合

2-2. 子系统角色
实现了子系统的功能
对它而言, facade 角色就和客户角色一样是未知的, 它没有任何 facade 角色的信息和链接

2-3. 客户角色
调用 facade 角色来完成要得到的功能

3. 应用
JavaEE 应用里使用业务逻辑组件 (Service) 来封装 DAO 组件也是典型的门面模式, 每个业务逻辑组件都是众多 DAO 组件的门面, 系统的控制器类无须直接访问 DAO 组件, 而是由业务逻辑方法来组合多个 DAO 方法以完成所需功能, 而 Action 只需与业务逻辑组件交互即可

[设计模式] 单例模式 (Singleton)

单例是一种创建性模型, 它用来确保只产生一个实例, 并提供一个访问它的全局访问点
单例模式确保某一个类只有一个实例, 而且自行实例化并向整个系统提供这个实例单例模式

1. 场景
1-1. 不分状态或只读状态的共享对象
当拥有一个没有状态之分并且有多个依赖对象的 bean
因为如果没有状态之分就不需要同步, 当每次某个依赖对象在工作中需要使用这个 bean 的时候就不需要创建一个新的实例

1-2. 使用共享状态的共享对象
如果 bean 的状态必须共享, 那么单例是最理想的选择
这种情况下要保证状态回写的同步操作的粒度越小越好

1-4. 具有可记录状态的高吞吐量对象
某个 bean 在程序中被大量使用, 共享对象只有少量的可回写状态, 而这时实例化新的实例将会耗费巨大资源, 那么也许会发现维护一个单例和其所有对象状态回写的同步, 与持续创建上百个该 bean 的实例相比能够提供更好的性能. 使用这种方法一定要努力保存同步的细粒度而不要牺牲一致性.

以下场景需要考虑使用非单例创建模式
1-5. 具有可回写状态的对象
如果 bean 拥有一些可回写状态, 这时会发现对它们进行同步操作要比为每个请求创建一个新的实例耗费的资源更多

1-6. 具有私有状态的对象
某个独立对象需要拥有它私有的状态, 这样它们能够与其所依赖的 bean 单独处理自己的操作, 这种情况下单例显然不合适, 需要使用非单例模式

2. 两种单例模式
懒汉方式: 全局的单例实例在第一次被使用时构建, 延迟初始化
饿汉方式: 全局的单例实例在类装载时构建, 急切初始化

3. 单例模式的缺陷及注意点
3-1. 多线程情况下, 懒加载模式可能导致线程不安全因素 (4-2-2, 4-2-3)
3-2. 当单例类被多个 classloader 加载的情况下, 可能获得多个单例类的实例 (TODO)
3-3. 当单例类实现了序列化接口 Serializer 时, 我们如果将对象序列化, 并反序列化得到实例时, 这个实例将是一个新的实例, 而不是序列化之前的实例 (5)

4. 实现
4-1. 饿汉模式
4-1-1. 适用于单线程与多线程
public class Singleton {
    private Singleton() {}
    private static Singleton instance = new Singleton();
    public static Singleton getInstance() {
        return instance;
    }
}

4-2 懒汉
4-2-1. 懒汉经典, 只适用于单线程
public class Singleton {
    private static Singleton instance = null;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

4-2-2. 懒汉改进 (1) 双重锁, 适用于多线程, volatile, JDK1.5 以上
public class Singleton {
    private volatile static Singleton instance = null;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

4-2-3. 懒汉改进 (2), 适用于多线程, 任何 JDK 版本
public class Singleton {
    private Singleton() {}
    private static class SingletonInstance {
        private static final Singleton instance = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonInstance.instance;
    }
}

4-3. 懒汉模式多线程测试
4-3-1. 待测类
4-3-1-1. 懒汉经典, 单线程成功, 多线程失败
public class Singleton {
    private static Singleton instance = null;
    private static boolean firstThread = true;
    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            simulateRandomActivity();
            instance = new Singleton();
        }
        System.out.println("created singleton: " + instance);
        return instance;
    }

    private static void simulateRandomActivity() {
        try {
            if (firstThread) {
                firstThread = false;
                System.out.println("sleeping...");

                // This nap should give the second thread enough time
                // to get by the first thread.
                Thread.currentThread().sleep(50);
            }
        } catch (InterruptedException ex) {
            System.out.println("Sleep interrupted");
        }
    }
}

4-3-1-2. volatile 改进, 单多线程均可
public class Singleton {
    private volatile static Singleton instance = null;
    private static boolean firstThread = true;
    private Singleton() {}

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

        System.out.println("created singleton: " + instance);
        return instance;
    }

    private static void simulateRandomActivity() {
        try {
            if (firstThread) {
                firstThread = false;
                System.out.println("sleeping...");
                Thread.currentThread().sleep(50);
            }
        } catch (InterruptedException ex) {
            System.out.println("Sleep interrupted");
        }
    }
}

4-3-1-3. 静态内部类, 单多线程均可 (推荐)
public class Singleton {
    private static boolean firstThread = true;
    private Singleton() {}
    private static class SingletonInstance {
        private static final Singleton instance = new Singleton();
    }

    public static Singleton getInstance() {
        simulateRandomActivity();
        Singleton instance = SingletonInstance.instance;
        System.out.println("created singleton: " + instance);
        return instance;
    }

    private static void simulateRandomActivity() {
        try {
            if (firstThread) {
                firstThread = false;
                System.out.println("sleeping...");
                Thread.currentThread().sleep(50);
            }
        } catch (InterruptedException ex) {
            System.out.println("Sleep interrupted");
        }
    }
}

4-3-2. 多线程测试类
import junit.framework.Assert;
import org.junit.Test;
public class SingletonTest {
    private static Singleton singleton = null;
    public void setUp() { singleton = null; }

    @Test
    public void testUnique() throws InterruptedException {
        // Both threads call Singleton.getInstance().
        Thread threadOne = new Thread(new SingletonTestRunnable());
        Thread threadTwo = new Thread(new SingletonTestRunnable());
        threadOne.start();
        threadTwo.start();
        threadOne.join();
        threadTwo.join();
    }

    private static class SingletonTestRunnable implements Runnable {
        public void run() {
            // Get a reference to the singleton.
            Singleton s = Singleton.getInstance();

            // Protect singleton member variable from
            // multithreaded access.
            synchronized (SingletonTest.class) {
                if (singleton == null)// If local reference is null...
                    singleton = s;// ...set it to the singleton
            }

            // Local reference must be equal to the one and
            // only instance of Singleton; otherwise, we have two
            // Singleton instances.
            Assert.assertEquals(true, s == singleton);
        }
    }
}

5. 单例类的串行化
5-1. 单例类
import java.io.Serializable;
public class Singleton implements Serializable {
    private static final long serialVersionUID = 1L;
    private static Singleton instance = new Singleton();
    private Singleton() {}

    public static Singleton getInstance() {
        return instance;
    }

    // 需要添加此方法
    private Object readResolve() {
        return instance;
    }
}

5-2. 测试类
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.NotSerializableException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import junit.framework.Assert;
import org.junit.Test;
public class SingletonTest {
    @Test
    public void testSerialize() {
        System.out.println("testing singleton serialization...");
        writeSingleton();
        Singleton s1 = readSingleton();
        Singleton s2 = readSingleton();
        Assert.assertEquals(true, s1 == s2);
    }

    private void writeSingleton() {
        try {
            FileOutputStream fos = new FileOutputStream("serializedSingleton");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(Singleton.getInstance());
            oos.flush();
        } catch (NotSerializableException se) {
            System.err
                    .println("Not Serializable Exception: " + se.getMessage());
        } catch (IOException iox) {
            System.err.println("IO Exception: " + iox.getMessage());
        }
    }

    private Singleton readSingleton() {
        Singleton s = null;

        try {
            FileInputStream fis = new FileInputStream("serializedSingleton");
            ObjectInputStream ois = new ObjectInputStream(fis);
            s = (Singleton) ois.readObject();
        } catch (ClassNotFoundException cnf) {
            System.err
                    .println("Class Not Found Exception: " + cnf.getMessage());
        } catch (NotSerializableException se) {
            System.err
                    .println("Not Serializable Exception: " + se.getMessage());
        } catch (IOException iox) {
            System.err.println("IO Exception: " + iox.getMessage());
        }
        return s;
    }
}

6. singleton registry class (TODO)
单例注册表类, 运行期间动态得到单例
解决懒汉饿汉类不能被继承的缺点

2011年12月23日金曜日

[设计模式] 适配器模式 (Adapter)

1. 场景
需要要复用以前的 "现存对象", 但是这些对象所提供的接口并不一定能适应我们的新环境, 就要对其转换成我们需要的接口, 来供我们调用

2. 角色
Target: 客户端希望得到的接口 (接口, 具体类, 抽象类)
Adaptee: 现存并希望重复使用的需要适配的类 (接口, 具体类, 抽象类)
Adapter: 把源接口转换成目标接口 (类)

3. 分类
Adapter 实现了客户需要的 Target 接口, 因此他可以满足 Client 的需要, 同时它又重用的原有系统的 Adaptee 类
Java 中有两种方式可以达到重用的目的, 继承和包含
在适配器模式中也可以通过这两种方式达到重用 Adaptee类 的目的, 根据重用使用方式的不同一般将适配器模式分为两类

3-1. 类适配器 (Class Adapter)
适配器类实现 Target 接口
适配器类通过继承来实现对 Adaptee 类的重用 (高耦合不推荐使用)
采用“多继承”的实现方式,带来了不良的高耦合,所以一般不推荐使用。对象适配器采用“”的方式,更符合松耦合精神

3-2. 对象适配器 (Object Adapter)
适配器类实现 Target 接口
适配器类通过组合来实现对 Adaptee类 的重用 (对象组合低耦合推荐使用)

4. 实例
Client: CellPhone
Target: CellPhonePoweProvidable
Adaptee: CurrentPower
Adapter: CellPhoneAdapter1, CellPhoneAdapter2

/**
 * Adapee
 */
public class CurrentPower {
    public String getPower() {
        System.out.println("get current power 220V");
        return "220V";
    }
}

/**
 * Target
 */
public interface CellPhonePoweProvidable {
    String getCellPhonePower();
}

/**
 * Class Adapter
 */
public class CellPhoneAdapter1 extends CurrentPower implements CellPhonePoweProvidable {
    @Override
    public String getCellPhonePower() {
        String currentPower = super.getPower();
        System.out.println("change power from 220V to 5V");
        String cellPhonePower = currentPower.replaceAll("220", "5");
        return cellPhonePower;
    }
}

/**
 * Object Adapter
 */
public class CellPhoneAdapter2 implements CellPhonePoweProvidable {
    private CurrentPower currentPower = null;
    public CellPhoneAdapter2(CurrentPower currentPower) {
        this.currentPower = currentPower;
    }

    @Override
    public String getCellPhonePower() {
        String currentPower = this.currentPower.getPower();
        System.out.println("change power from 220V to 5V");
        String cellPhonePower = currentPower.replaceAll("220", "5");
        return cellPhonePower;
    }
}

/**
 * Client
 */
public class CellPhone {

    public static void main(String[] args) {
        CellPhone cellPhone = new CellPhone();

        //
        CellPhoneAdapter1 adpter1 = new CellPhoneAdapter1();
        cellPhone.charge(adpter1);

        System.out.println("------------------------------------------");

        //
        CellPhoneAdapter2 adpter2 = new CellPhoneAdapter2(new CurrentPower());
        cellPhone.charge(adpter2);
    }

    private void charge(CellPhonePoweProvidable adpter) {
        String power = adpter.getCellPhonePower();
        System.out.println("cellphone is charging to use " + power);
    }
}

2011年12月18日日曜日

[JAVA] 输出流转为输入流

1. 字节流
1-1. 使用内存中的字节数组过渡 (需要足够大的内存)
byte[] src = { 0x0001 };
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(src);
byte[] des = baos.toByteArray();
ByteArrayInputStream bais = new ByteArrayInputStream(des);

1-2. 使用管道流
final byte[] src = { 0x0041, 0x0042 };
final PipedInputStream in = new PipedInputStream();
final PipedOutputStream out = new PipedOutputStream(in);
new Thread(new Runnable() {
    public void run() {
        try {
            out.write(src);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}).start();

int b = -1;
while ((b = in.read()) != -1) {
    System.out.println(b);
}

1-3. 使用循环缓存 (外部 util) (http://ostermiller.org/utils/download.html)
1-3-1. 多线程
CircularByteBuffer cbb = new CircularByteBuffer();
new Thread(new Runnable() {
    public void run() {
        class1.putDataOnOutputStream(cbb.getOutputStream());
    }
}).start();
class2.processDataFromInputStream(cbb.getInputStream());

1-3-2. 单线程
CircularByteBuffer cbb =
    new CircularByteBuffer(CircularByteBuffer.INFINITE_SIZE);
class1.putDataOnOutputStream(cbb.getOutputStream());
class2.processDataFromInputStream(cbb.getInputStream());

2. 字符流
2-1. 使用缓存数组
StringWriter out = new StringWriter();
class1.putDataOnWriter(out);
class2.processDataFromReader(new StringReader(out.toString()));

2-2. 使用管道
PipedReader in = new PipedReader();
PipedWriter out = new PipedWriter(in);
new Thread(new Runnable() {
    public void run() {
        class1.putDataOnWriter(out);
    }
}).start();
class2.processDataFromReader(in);

2-3. 使用循环缓存 (外部 util)
2-3-1. 多线程
CircularCharBuffer ccb = new CircularCharBuffer();
new Thread(new Runnable() {
    public void run() {
        class1.putDataOnWriter(ccb.getWriter());
    }
}).start();
class2.processDataFromReader(ccb.getReader());

2-3-1. 单线程
CircularCharBuffer ccb =
    new CircularCharBuffer(CircularCharBuffer.INFINITE_SIZE);
class1.putDataOnWriter(ccb.getWriter());
class2.processDataFromReader(ccb.getReader());