java类 设计模式_Java8中的设计模式(一)

之前在infoq上看到一篇文章:

swift版本原文参考:

http://www.infoq.com/cn/articles/design-patterns-in-swift

于是想着把这篇文章修改为Java8的版本,本文是对原文的Java8版本的修改,所以大部分文章和示例都是采用原文的叙述;再次表达对原文作者的感谢;

设计模式##

设计模式(Design Pattern)是对软件设计中普遍存在的各种问题,所提出的解决方案。这个术语是由埃里希·伽玛等人(Erich Gamma,Richard Helm,Ralph Johnson和John Vlissides这四人提出的。也被称为:Gang of Four,GOF,四人帮)在1990年代从建筑设计领域引入到计算机科学的。 设计模式并不能直接用于完成代码的编写,而是描述在各种不同情况下,要怎么解决问题的一种方案。

以上描述摘自维基百科。

随着我们所使用的编程语言的演化,我们遇到的问题也确实一直在改变。GOF提出的23种设计模式也许部分过时了。但我们遇到的问题并不会消失,设计模式的概念将一直存在:提炼普遍存在的问题,提出解决方案。这其实是一个抽象过程。现在已有的编程语言都存在表达的局限,即对某类问题抽象层次过低。所以,我们在使用任何编程语言时,都还是会遇到一些普遍存在的却没有被语言本身很好解决的问题。这时,我们就会使用到“设计模式”,即人们总结出来的解决方案:遇到问题A,用方案A;遇到问题B,用方案B。只不过问题会一直变化。现在我们不可以再使用那23个模式来解决问题了,但是我们仍然需要总结出其它模式来解决新的问题。这种情况一直会持续到我们拥有“完美语言”的那一天。但现在看起来,这一天还没有到来的迹象。

Java8中的设计模式##

Java8中提出了函数式编程等新的概念,使得Java8在面对传统的GOF23模式的时候,解决问题的方式已经得到了部分的改善,之前的一些设计模式在Java8面前就已经不再是问题了。但是Java8提出了函数式编程的同时,因为涉及到向后兼容的问题,同样在Java8中可能面对新的代码设计问题,比如接口中的默认方法带来的诸多设计细节,同样也是会引人深思的;

下面主要讨论的是,在Java8中,传统的哪些开发模式被消除了或者以一种更简单的方式简化了。

一,命令模式##

命令模式使用对象封装一系列操作(命令),使得操作可以重复使用,也易于在对象间传递。首先来一个传统Java方式实现的命令模式代码。

public class Light {

public void on() {

System.out.println("light turn on");

}

public void off() {

System.out.println("light turn off");

}

}

interface Command {

void execute();

}

class FilpUpCommand implements Command {

private Light light;

public FilpUpCommand(Light light) {

this.light = light;

}

public void execute() {

light.on();

}

}

class FilpDownCommand implements Command {

private Light light;

public FilpDownCommand(Light light) {

this.light = light;

}

public void execute() {

light.off();

}

}

以上代码中,灯(Light)是命令(Command)的操作对象(Receiver)。我们定义了命令的协议,同时我们实现两个具体的命令操作:FlipUpCommand和FlipDownCommand。它们分别使灯亮,和使灯灭。

class LightSwitch{

private List queue=new ArrayList<>();

public void addCommand(Command cmd){

queue.add(cmd);

}

public void execute(){

for(Command cmd:queue){

cmd.execute();

}

}

}

class Client{

public static void pressSwitch(){

Light lamp=new Light();

Command flipUpCommand=new FilpUpCommand(lamp);

Command flipDowomnCmand=new FilpDownCommand(lamp);

LightSwitch lightSwitch = new LightSwitch();

lightSwitch.addCommand(flipUpCommand);

lightSwitch.addCommand(flipDowomnCmand);

lightSwitch.addCommand(flipUpCommand);

lightSwitch.addCommand(flipDowomnCmand);

lightSwitch.execute();

}

}

上面的代码首先创建了一个命令执行者LightSwitch,并创建一个客户对象来使用命令;

在函数式编程中,由于存在高阶函数。我们可以直接将一个函数作为参数传给另外一个函数。所以,使用类包裹函数在对象间传递这件事情就显得多余了。以下代码显示如何使用高阶函数达到命令模式相同的效果:

class LightSwitchFP {

private List> queue = new ArrayList<>();

public void addCommand(Consumer cmd) {

queue.add(cmd);

}

public void execute(Light light) {

for (Consumer cunsumer : queue) {

cunsumer.accept(light);

}

}

}

class Client {

public static void pressSwitch() {

Light lamp = new Light();

Consumer flipUp = light -> {light.on();};

Consumer flipDown = light -> {light.off();};

LightSwitchFP lightSwitch = new LightSwitchFP();

lightSwitch.addCommand(flipUp);

lightSwitch.addCommand(flipDown);

lightSwitch.addCommand(flipUp);

lightSwitch.addCommand(flipDown);

lightSwitch.execute(lamp);

}

}

在Java8中,首先我们直接使用Java8提供的Consumer函数接口作为我们的命令接口,因为有了lambda表达式,我们根本无需在单独为具体命令对象创建类型,而通过传入labmda表达式来完成具体命令对象的创建;

二,策略模式##

策略模式定义了一系列算法,将每个算法封装起来,并且使它们之间可以互相替换。此模式让算法的变化独立于使用算法的客户。

下面简单演示一个传统的策略模式实现方案:

interface Strategy {

public Integer compute(Integer op1, Integer op2);

}

class Add implements Strategy {

public Integer compute(Integer op1, Integer op2) {

return op1 + op2;

}

}

class Multiply implements Strategy {

public Integer compute(Integer op1, Integer op2) {

return op1 * op2;

}

}

class Context {

private Strategy strategy;

public Context(Strategy strategy) {

this.strategy = strategy;

}

public void use(Integer first, Integer second) {

System.out.println(this.strategy.compute(first, second));

}

}

类似于命令模式,策略模式中的策略对象主要用于封装操作(函数),不同的是策略模式中的策略对象封装的是不同的算法。这些算法实现了相同的接口,在这个例子中,接口是用Strategy协议表示的。我们使用两个实现了Strategy协议的具体类:Add和Multiply分别封装两个简单的算法。Context对象,用于对算法进行配置选择,它有一个Strategy类型的实例变量:strategy。通过配置Context的strategy具体类型,可以使用不同的算法。

然后我们再看看怎么简化策略模式:

public static final BinaryOperator add = (op1, op2) -> op1 + op2;

public static final BinaryOperator multiply = (op1, op2) -> op1 * op2;

class ContextFP {

private BinaryOperator strategy;

public ContextFP(BinaryOperator strategy) {

this.strategy = strategy;

}

public void use(Integer first, Integer second) {

System.out.println(strategy.apply(first, second));

}

}

public static void main(String[] args) {

StraFP fp = new StraFP();

ContextFP ctx = fp.new ContextFP(StraFP.add);

ctx.use(1, 2);

}

在Java8中,我们很自然的使用内建的function interface作为封装算法的载体,这样更为直接自然。例子中,ContextFP的构造器的传参就是函数类型。给予构造器代表不同算法的函数,就配置了不同的算法。

函数也可以作为类的实例变量。这样在类中,直接维护代表算法的函数也成为可能。从类型声明可以看出,ContextFP中的实例变量strategy就是一个函数。

一等函数的概念使得函数获得了更高的地位,使得函数的灵活性大大增加。在很多场景下直接使用函数会是更直接自然的选择。面向对象编程范式,赋予了对象更高的地位。但是,如果给予函数“正常”一些的地位,可以简化不少问题。设计模式中的不少模式存在都是由于函数的使用限制,需要使用在使用类包裹函数。类似的例子还有模版方法模式(Template method)。

上面代码示例不能对策略做很好的封装,下面提供了一个枚举的版本:

public enum StrategyEnum {

ADD(() -> (x, y) -> x + y),

MULTIPLY(() -> (x, y) -> x * y);

private Supplier> operation;

private StrategyEnum(Supplier> operation) {

this.operation = operation;

}

public BinaryOperator get() {

return operation.get();

}

}

class ContextFP {

private StrategyEnum strategy;

public ContextFP(StrategyEnum strategy) {

this.strategy = strategy;

}

public void use(Integer first, Integer second) {

System.out.println(strategy.get().apply(first, second));

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值