Java 设计模式

一 单例模式

解决的问题:就是可以保证一个类在内存中的对象唯一性。

public class SingleInstance {

    private static volatile SingleInstance singleInstance = null;

    private SingleInstance() {

    }

    public SingleInstance getInstance() {

        if (singleInstance == null) { //A

            synchronized (SingleInstance.class) {

                if (singleInstance == null) {

                    singleInstance = new SingleInstance(); //B

                }

            }

        }

        return singleInstance;

    }

}

Note 1:添加volatile修饰避免B处指令重排序时其他线程拿到B处可能还未初始化的单例对象(完成了内存分配,引用的赋值 还未来得及进行对象的初始化)然后在A处判断单例对象不为空直接返回的未初始化的对象。

Note 2: synchronized关键字保证了临界区内变量的可见性和原子性,但无法保证临界区之外访问的变量的原子性。如果对单例对象的读写完全在临界区内,那么就不会出现线程安全问题。不过,这样就带来了性能问题。

Note 3:共享的单例对象(不论是否静态)在各个线程中是同一个

Note 4:通过非枚举类设计的单例模式均存在反射破解获取设置构造函数非私有进而构造多个实例的问题。

二 策略模式: 定义一组算法,将每个算法都分装起来,并使他们之间可以互换。 策略模式就是使用的面向对象思想中的继承和多态的机制

 

public interface Strategy {

    void doSomething();

}

public class StrategyOne implements Strategy{

    @Override

    public void doSomething() {

        System.out.println("StrategyOne do Something ...");

    }

}

public class StrategyTwo implements Strategy{

    @Override

    public void doSomething() {

        System.out.println("StrategyTwo do Something ...");

    }

}

public class Context {

    private Strategy strategy;

    public Context (Strategy strategyInfo){

        this.strategy = strategyInfo;

    }

    public void doSomething(){

        strategy.doSomething();

    }

}

@Test

    public void testStrategy() {

        StrategyOne strategyOne = new StrategyOne();

        StrategyTwo strategyTwo = new StrategyTwo();

        Context context = new Context(strategyTwo);

        context.doSomething();

}

策略模式的优点:

Note1:算法可以自由切换,通过Context类对策略成员进行封装,保证对外提供"可自由切换”的策略。

Note2:避免使用多重条件判断,

Note3:扩展性良好

策略模式的缺点:

Note1:策略类数量太多:每一个策略都是一个类,复用性很小,类数量增多。

Note1:所有策略类都需要向外暴露:客户端必须知道有哪些策略才能决定使用哪一个策略。导致封装类没有意义,可以使用工厂模式解决。

应用:

我们假设有如下场景:

我们使用聊天工具聊天时,可以发送点对点消息(私聊)和点对多消息(群聊),而发送不同的消息执行的操作是不一样的,也就是说我们在不同的场景下(私聊或者 群聊)发送消息时会调用不同的发送方法,但是我们只有一个消息发送器(可以理解为发送消息时的发送按钮),我们希望可以通过这消息发送器发送任何类型的消 息。

三 工厂模式:专门负责将大量有共同接口的类实例化,工厂模式可以动态的决定将哪一个类实例化,不必事先知道每次要实例化哪一个类。

简单工厂模式

 

public interface Fruit {

    public void doSomething();

}

public class Apple implements Fruit {

    @Override

    public void doSomething() {

        System.out.println("Apple do something !!!");

    }

}

public class Orage implements Fruit {

    @Override

    public void doSomething() {

        System.out.println("orage do something !!!");

    }

}

public class FruitFactory {

    public static Fruit productFruit(String fruitName) {

        if ("Apple".equals(fruitName)) {

            return new Apple();

        } else {

            return new Orage();

        }

    }

}

@Test

    public void testFactory() {

        Fruit fruit = FruitFactory.productFruit("Apple");

        fruit.doSomething();

}

简单工厂模式的优点与缺陷:

优点:模式的核心是工厂,这个类含有必要的判断逻辑,可以决定在什么时候创建哪一个产品的实例。而客户端可以免除直接创建产品对象的责任,而仅仅负责"消费"产品。实现了对责任的额分割。

缺陷:工厂类是一个全能类,像上帝类,里面的方法是静态的,无法由子类继承,扩展性比较差,当有新的产品加入到系统中去,就需要修改工厂类。对"开-闭"

多态工厂模式:工厂方法设计模式:弥补了简单工厂对“开-闭”原则支持不够的缺点,核心的工厂类不在负责所有产品的创建,而将具体创建工作交给了子类去做,这个核心类变成了一个抽象工厂角色。仅负责给出具体工厂子类必须的实现接口,而不接触那个产品类应该被实例化的这种细节。这样当系统需要加入新的产品的时候,不需要修改客户端,也没必要修改抽象工厂类或者其他已有的具体工厂角色,只需要向系统加入一个产品类以及它对应的工厂类。

 

public interface FruitFactory {

    public Fruit factory();

}

public class AppleFactory implements FruitFactory {

    @Override

    public Fruit factory() {

        return new Apple();

    }

}

@Test

    public void testFactory() {

        AppleFactory appleFactory = new AppleFactory();

        Fruit fruit = appleFactory.factory();

        fruit.doSomething();

}

四 动态代理: 动态代理是指在实现阶段不需要关心代理谁,而在运行阶段才指定代理哪一个对象。Spring AOP采用的核心思想就是动态代理设计模式

 

public interface IplayGame {

    public void killBoss();

    public void updateLevel();

}

public class PlayGameImpl implements IplayGame {

    private String name;

    public PlayGameImpl(String name) {

        this.name = name;

    }

    @Override

    public void killBoss() {

        System.out.println(name + "kill Boss");

    }

    @Override

    public void updateLevel() {

        System.out.println(name + "update Level");

    }

}

public class PlayGameHandle implements InvocationHandler {

    private Object target;

    public PlayGameHandle(Object target) {

        this.target = target;

    }

    @Override

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        //before do something

        Object invoke = method.invoke(target, args);

        if(method.getName().equalsIgnoreCase("login")){

            System.out.println("有人在用你的账号登陆,请确认是否是本人!");

        }

        return invoke;

        //after do something

    }

}

@Test

    public void testProxy() {

        PlayGameImpl playGame = new PlayGameImpl("zhang san");

        ClassLoader classLoader = playGame.getClass().getClassLoader();

        Class<?>[] interfaces = playGame.getClass().getInterfaces();

        IplayGame iplayGame = (IplayGame) Proxy.newProxyInstance(classLoader, interfaces, new PlayGameHandle(playGame));

        iplayGame.killBoss();

}

总结:在被代理对象方法前后加上了业务逻辑,面向切面编程的体现。

public final class $Proxy11 extends Proxy 

    implements IGamePlayerService

    // 构造方法,参数就是刚才传过来的GamePlayerhandler类的实例 

    public $Proxy11(InvocationHandler invocationhandler) 

    { 

        super(invocationhandler); 

    } 

  …

    /**

     * 这个方法是关键部分

     */ 

    public final void killBoss() 

    { 

        try 

        { 

            // 实际上就是调用GamePlayerhandler的public Object invoke(Object proxy, Method method, Object[] args)方法,第二个问题就解决了 

            super.h.invoke(this, m3, null); 

            return; 

        } 

private static Method m1; 

    private static Method m3; 

    private static Method m0; 

    private static Method m2; 

    // 在静态代码块中获取了4个方法:Object中的equals方法、IGamePlayerService中的login、killBoss、upgrade方法,Object中的hashCode方法、Object中toString方法 

    static  

    { 

        try 

        { 

            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {  

                Class.forName("java.lang.Object") 

            }); 

            m3 = Class.forName("dynamic.proxy.IGamePlayerService").getMethod("killboss", new Class[0]); 

       m4 = Class.forName("dynamic.proxy.IGamePlayerService").getMethod("login", new Class[0]);

       m5 = Class.forName("dynamic.proxy.IGamePlayerService").getMethod("upgrade", new Class[0]);

            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); 

            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); 

        } 

五 装饰模式: 又名包装模式(Wrapper),以对客户端透明的方式扩展对象的功能,是继承关系的一种替代关系。装饰模式以对客户端透明的方式动态的给一个对象附加上更多的责任。换言之,客户端不会觉得在装饰前和装饰后有什么不同。

 

public interface Component {

    public void coding();

}

public class Decorator implements Component {

    @Override

    public void coding() {

        System.out.println("coding is easy easy easy !!!");

    }

}

public class DecoratorOne extends Decorator {

    private Component component;

    public DecoratorOne(Component component) {

        this.component = component;

    }

    @Override

    public void coding() {

        System.out.println("DecoratorOne do before ....");

        component.coding();

    }

}

public class DecoratorTwo extends Decorator {

    private Component component;

    public DecoratorTwo(Component component) {

        this.component = component;

    }

    @Override

    public void coding() {

        System.out.println("DecoratorTwo do after ....");

        component.coding();

    }

}

public class ComponentToDec implements Component {

    @Override

    public void coding() {

        System.out.println("just the bigin ...");

    }

}

@Test

    public void testDecorator() {

        Component componentToDec = new ComponentToDec();

        Component decoratorOne = new DecoratorOne(componentToDec);

        DecoratorTwo decoratorTwo = new DecoratorTwo(decoratorOne);

        decoratorOne.coding();

        decoratorTwo.coding();

    }

优点:

装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。装饰模式在系统运行期动态的决定给装饰的对象动态的“贴上”一个需要的“装饰”或者除掉一个不需要的“装饰”。继承则不同,继承关系式静态的,它在系统运行前就决定了。

通过使用不同的具体装饰类(ManagerDecoratorA,ManagerDecoratorB,C。。。。。),以及这些装饰类的排列组合,设计师么可以设计出很多不同的行为组合。(ManagerDecoratorB装饰ManagerDecoratorA,或者用ManagerDecoratorA装饰ManagerDecoratorB)

缺点:

使用装饰模式会产生比使用继承关系更多的对象,更多的对象使得查错变得困难,特别是这些对象看上去都很像。装饰模式比继承产生更少的类。

应用:JAVA I/O库

六 组合模式

优点:

 *1.各个叶子节点足够内聚

 *2.客户端实现简单,可以一直的使用组合对象和单个对象

 * 3.可以面向顶层接口,统一做一些事情(eg.是否可降级,是否可异步处理,日志aop等等)

 * 4.对扩展开放,很容易的可以在组合对象内加入单个对象, 而客户端不必修改原有代码

* 使用场景:

* 1.想表示对象的整体和部分的层次,并且在此结构中,希望通过一种方式忽略整体和部分的差异

 * 2.希望用户忽略组合对象和单个对象的不同,用户将统一的使用结构中的所有对象。

 * 3.可以结合责任链模式,迭代器,装饰器,享元,访问者模式一块使用。

public interface ComponentFile {

    public boolean isFile();

    public void operate();

}

public class ComponentFolder implements ComponentFile {

    List<ComponentFile> componentFiles = new ArrayList<ComponentFile>();

    @Override

    public boolean isFile() {

        return true;

    }

    @Override

    public void operate() {

        for (ComponentFile item : componentFiles) {

            if (item.isFile()) {

                item.operate();

            } else {

                System.out.println(item.getClass() + " is not file...");

            }

        }

        System.out.println("ComponentFolder operate ...");

    }

    public void addComponent(ComponentFile componentFile) {

        componentFiles.add(componentFile);

    }

}

public class ComponentFileImage implements ComponentFile {

    @Override

    public boolean isFile() {

        return false;

    }

    @Override

    public void operate() {

        System.out.println("ComponentFileImage operate...");

    }

}

public class ComponentFileText implements ComponentFile {

    @Override

    public boolean isFile() {

        return true;

    }

    @Override

    public void operate() {

        System.out.println("ComponentFileText operate...");

    }

}

@Test

    public void testComponentFile() {

        ComponentFolder componentFolder = new ComponentFolder();

        ComponentFile componentFileImage = new ComponentFileImage();

        ComponentFile componentFileText = new ComponentFileText();

        componentFolder.addComponent(componentFileImage);

        componentFolder.addComponent(componentFileText);

        componentFolder.operate();

}

七 命令模式

模式的组成:

* 抽象命令类(Command):声明执行操作的接口。调用接收者TVReceiver响应的操作,以实现执行的方法execute。

* 具体命令类(TvChangeCommand,TvCloseCommand,TvOpenCommand):创建一个具体命令对象并设定它的接收者。通常会持有接收者,并调用接收者的功能来完成命令要执行的操作

* 调用者(RemoteControllerInvoke 遥控器调用类,请求的发送者):要求该命令执行这个请求,通常会持有命令对象,可以持有很多的命令对象。

* 接收者(TVReceiver 电视机,请求的接收者): 知道如何实施和执行一个请求相关的操作。任何类都可能作为一个接收者,只要它能够实现命令要求实现的功能。

* 客户端(ConmandClient):创建具体的命令对象,并且创建设置命令的接收者。真正的使用命令客户端是从RemoteControllerInvoke来触发执行的。

优点:

* 1.降低系统的耦合度:将调用操作的对象与知道如何实现该操作的对象解耦

 * 2.组合命令:可以将多个命令分配成一个组合命令,可以比较容易的设计一个命令队列和宏命令。

 * 3.符合开闭原则:增加新的具体的Command很容易,无需改变现有的类

 * 4.可以方便的实现对请求或者说命令的Undo撤销和Redo恢复

public interface Command {

    public void operate();

}

public class ChangeTvCommand implements Command {

    private TvReciver tvReciver;

    public ChangeTvCommand(TvReciver tvReciver){

        this.tvReciver = tvReciver;

    }

    @Override

    public void operate() {

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

        tvReciver.changeTvCommand();

    }

}

public class CloseTvCommand implements Command {

    private TvReciver tvReciver;

    public CloseTvCommand(TvReciver tvReciver){

        this.tvReciver = tvReciver;

    }

    @Override

    public void operate() {

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

        tvReciver.closeTvCommand();

    }

}

public class TvReciver {

    public void changeTvCommand(){

        System.out.println("TvReciver changeTvCommand ...");

    }

    public void closeTvCommand(){

        System.out.println("TvReciver closeTvCommand ...");

    }

}

public class CommandHandle implements Command {

    private Command command;

    public CommandHandle(Command command) {

        this.command = command;

    }

    @Override

    public void operate() {

        command.operate();

    }

}

@Test

    public void testCommand() {

        TvReciver tvReciver = new TvReciver();

        ChangeTvCommand changeTvCommand = new ChangeTvCommand(tvReciver);

        CloseTvCommand closeTvCommand = new CloseTvCommand(tvReciver);

        CommandHandle changeTvCommandHandle = new CommandHandle(changeTvCommand);

        changeTvCommandHandle.operate();

        CommandHandle closeTvCommandHandle = new CommandHandle(closeTvCommand);

        closeTvCommandHandle.operate();

    }

八 责任链模式:

    抽象处理者(BuyPower)角色:定义个处理请求的接口。此角色由java的抽象类和java接口实现。

具体处理者角色(PresidentBuyPower):具体处理这街道请求之后,可以选择将请求处理掉,也可以将请求传给下家。由于具体处理者持有对下家的引用,因此,如果需要,具体处理这可以访问下家。

public class BuyRequest {

    private long money;

    private String applyer;

}

public abstract class ResponsChain {

    protected ResponsChainTwo responsChainTwo;

    public void setResponsChainTwo(ResponsChainTwo responsChainTwo) {

        this.responsChainTwo = responsChainTwo;

    }

    public abstract void buyPower(BuyRequest buyRequest);

}

public class ResponsChainOne extends ResponsChain {

    private static final int range = 100;

    @Override

    public void buyPower(BuyRequest buyRequest) {

        if (buyRequest.getMoney() < range) {

            System.out.println("ResponsChainOne say buy buy buy");

        } else {

            responsChainTwo.buyPower(buyRequest);

        }

    }

}

public class ResponsChainTwo extends ResponsChain {

    private static final int range = 1000;

    @Override

    public void buyPower(BuyRequest buyRequest) {

        if (buyRequest.getMoney() < range) {

            System.out.println("ResponsChainTwo say buy buy buy");

        } else {

            System.out.println("ResponsChainTwo say no money");

        }

    }

}

@Test

    public void testResponsChain() {

        BuyRequest buyRequest = new BuyRequest(9900, "zhangsan");

        ResponsChainOne responsChainOne = new ResponsChainOne();

        ResponsChainTwo responsChainTwo = new ResponsChainTwo();

        responsChainOne.setResponsChainTwo(responsChainTwo);

        responsChainOne.buyPower(buyRequest);

    }

使用场景:申请经费,团建。

申请团建经费的大致流程一般是:申请人先填写申请单,然后交给领导审批,如果申请批准下来的话,领导通知申请人审批通过还是未通过。

不同的级别的领导,对于审批的额度不一样,比如经理只能审批5000以内的申请,主管只能申请10000以内的申请......

参考:https://www.cnblogs.com/200911/p/3907054.html

原文地址:https://www.cnblogs.com/leifonlyone/p/12367076.html