最近在看《Think In JAVA》,其中在讲解继承,组合,抽象类和接口的时候,提到了题中的几个设计模式。这几个设计模式也确实让我更好的理解了JAVA中各个数据结构的含义。今天就结合书本还有自己的理解,稍微整理一下这几个设计模式。

Strategy Pattern | 策略模式

这里就必须要提一下向上转化这个概念。在继承和接口中都有提到这个概念。
向上转化在继承中是指子类可以向上转化为父类。比如,有一个Instrument类,以及它的一个子类Flute。子类重写的play方法会覆盖父类的方法。

    class Instrument{public void play(){...//play instrument}}class Flute extends Instrument{public void play(){...//play flute}}

如果这时有一个方法需要接收一个乐器参数并演奏,那么无需写多个重载方法接收各种不同的乐器,只需要一个接收Instrument类的方法。

    public void play(Instrument instrument){instrument.play();}

在这个方法中传入一个Flute对象,调用的将是flute中的play方法。这就是向上转化,动态绑定的一个最简单的例子。
其实接口也是同理,只是接口允许多种向上转化。也就是说,JAVA中继承是唯一的,而接口是可以Implement多个的。因此JAVA中继承向上转化的路径唯一,而接口向上转化路径不唯一。

接下来就要讲到策略模式。

策略模式的概念如下:

Defines a set of encapsulated algorithms that can be swapped to carry out a specific behaviour
定义了一组封装好的算法,这些算法分别执行不同的操作。在实际运行中,这些算法可以动态切换来满足不同场景下的需求

策略模式的使用情景有:

  1. 将文件保存为不同的格式

  2. 排序算法的多种实现

  3. 文件压缩的多种实现

也就是说,策略模式将一组完成相同工作的不同方式的代码分别放到不同的类中,并通过策略模式实现在运行中的相互切换。

这是从网上找到的关于策略模式的UML图。

策略模式是JAVA中继承,抽象类以及接口的一种综合应用。

在策略模式中,我们可以根据一个“开放接口”设计出多种“具体策略”,然后在调用时只需要输入“开放接口”,程序运行时会根据“开放接口”的具体实现来决定具体的运行结果。

上面设计模式的代码如下:

//接口
public interface Strategy {public int doOperation(int num1, int num2);
}//接口实现类
public class OperationAdd implements Strategy{@Overridepublic int doOperation(int num1, int num2) {return num1 + num2;}
}
public class OperationSubstract implements Strategy{@Overridepublic int doOperation(int num1, int num2) {return num1 - num2;}
}
public class OperationMultiply implements Strategy{@Overridepublic int doOperation(int num1, int num2) {return num1 * num2;}
}//上下文
public class Context {private Strategy strategy;public Context(Strategy strategy){this.strategy = strategy;}public int executeStrategy(int num1, int num2){return strategy.doOperation(num1, num2);}
}//具体调用
public class StrategyPatternDemo {public static void main(String[] args) {Context context = new Context(new OperationAdd());        System.out.println("10 + 5 = " + context.executeStrategy(10, 5));context = new Context(new OperationSubstract());        System.out.println("10 - 5 = " + context.executeStrategy(10, 5));context = new Context(new OperationMultiply());        System.out.println("10 * 5 = " + context.executeStrategy(10, 5));}
}

使用策略模式与不使用策略模式的差距

下面我再讲一个具体的例子来说明使用策略模式与不使用策略模式的差距。

假设我们有一个压缩文件的功能,压缩文件有多种算法,如Zip,RAR等等。程序能够根据实际的操作系统以及性能等参数来选择一个压缩算法执行压缩操作。我们假设这个选择具体算法的功能放置CompressionPreference类中。

其实这些对于客户端来说都是透明的。也就是说,客户端只知道会有一个压缩功能,该功能需要客户上传要压缩的文件。如此场景下,服务端只需要提供一个压缩的接口,而无需暴露具体的实现。

代码如下:

//选择压缩方法类,根据具体情况返回压缩的方法
public class CompressionPreference{public static CompressionStrategy getPreferedStrategy(){//根据系统的情况或是用户的选择返回具体的压缩算法}
}//压缩策略接口
public interface CompressionStrategy{void compress(List<File> files);
}//压缩策略的具体实现
public class ZipCompressionStrategy implements CompressionStrategy{@Overridepublic void compress(List<File> files){//zip 压缩}
}public class RarCompressionStrategy implements CompressionStrategy{@Overridepublic void compress(List<File> files){//RAR 压缩}
}public class CompressionContext{private CompressionStrategy strategy;//这里根据CompressionPreference选择压缩策略public void setStrategy(CompressionStrategy strategy){this.strategy=strategy);}public void createArchieve(List<File> files){strategy.compress(files);}
}//客户端调用
public class Client{public static void main(String[] args) {CompressionContext ctx = new CompressionContext();//设置压缩上下文ctx.setCompressionStrategy(CompressionPreference.getPreferedStrategy());ctx.createArchive(fileList);}
}

通过这样的设计之后,如果需要添加新的算法,只需要增加一个CompressionStrategy的具体实现类,以及修改一下CompressionPreference中的方法即可。对于客户端的调用不会产生任何影响。

如果不对算法进行封装,直接允许客户端调用的话。

一方面,暴露了压缩算法的种种实现,

另一方面,也增加了可能造成的错误调用。

而且一旦增加新的压缩算法,客户端也需要知道这些本不需要知道的东西,调整自己的调用。这样的代码,可维护性实在是太差了。

适配器模式 | Adapter Design Pattern

定义:

Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.
将两个不相兼容的接口通过适配器进行相互转化。

适配器模式:是在想使用一个已经存在的类,但是他的接口并不符合要求,因为在编码过程中要遵循对扩展开放,对修改关闭的原则,所以不能对原有的类进行修改,这时便需要使用适配器模式,将原有的类适配成自己需要的形式。有类适配器和对象适配器两种适配器。

适配器模式要比策略模式要好理解一些。在书中讲解适配器模式时,实际上是为了补充说明如何面向接口编程。适配器模式,顾名思义,就是将本来并不继承某个接口的类通过适配器转化为可以通过该接口调用,它充当着连个不兼容的接口之间的桥梁

从原含义上来讲,适配器是指一个接口转换器,在生活中最常见的接口转换器就是你的手机充电线头啦!充电头将从插座中输出的标准220V电压(国内)转化为可以安全充电的电压。并且在另一侧提供了一个USB充电口,从而使手机可以在一切含有USB端口的充电线下进行充电。再举一个例子,也就是SD卡。使用相机的朋友知道,有些电脑是不提供SD卡接口的,那么就需要将SD卡插入SD卡读卡器,再将读卡器通过USB接口插入电脑。这时电脑就可以读取SD卡中的内容了。

在书中的例子,适配器应用的场景是将不可以修改的类改为继承某个接口从而可以作为该接口的一个实现类被调用

书中的适配器模式有两种实现方式,一种是通过代理,另一种是通过继承。两种方式本质上是相同的,如果需要原类中的所有实现,则通过继承方式实现适配器,如果只是一部分实现,则通过代理的方式。具体情况具体分析。


以上讲的都太过抽象了,下面讲一个具体的例子。

比如我有一个扫描类Scanner,他有一个方法,可以接收所有继承了Readable接口的类,并根据类中的情况将其中的数据读取出来。系统中已经有一些类,他们是可以被Scanner读取的,但是他们并没有继承Readable接口。

本着开闭原则,我可以给这些类添加一个适配器,使其可以被Scanner读取。通过这种模式,无论是出现新的Scanner读取文件或是读取系统已有的文件,都不必修改Scanner方法。只需要使其支持Readable接口就行。

    public class Scanner{public void read(Readable material){material.read();}}public interface Readable{void read();}public class TXT implements Readable{...public void read(){//读取txt文件}...}public class HTML{public void toReadableFormat(){//html文件也可以被读取,但是它并没有继承Readable接口,所以无法被Scanner识别}}//这里才是适配器模式public class HTMLAdapter implements Readable{...private HTML html;public HTMLAdapter(HTML html){this.html = html}public void read(){html.toReadableFormat();}...}//这时候两个文件都可以被读取了public class Test{public static void main(String[] args){Scanner s = new Scanner();s.read(new TXT());s.read(new HTMLAdapter(new HTML()));}}

一个例子不够,再来一个~

在自媒体的发展史中,媒体的格式越来越多样化,从最初的文本,到MP3,再到视频格式。如果现在有一个系统,它本来只支持MP3格式的文件的读取,这时候要想该系统可以支持新媒体类的文件的播放。

新媒体类文件由另一个团队开发,拥有自己的开发接口和具体实现。如何才能将该模块融入到现有系统中呢?

这时候就需要通过适配器模式来解决这个问题了。

这是这个系统给的UML类图。通过在原系统中新建一个MediaAdapter适配器继承原媒体播放器的接口,从而使原系统可以在不知下层变动的基础上,继续调用原来的play方法来实现播放功能。
具体代码如下:

    public interface MediaPlayer {public void play(String audioType, String fileName);}public interface AdvancedMediaPlayer {    public void playVlc(String fileName);public void playMp4(String fileName);}public class VlcPlayer implements AdvancedMediaPlayer{@Overridepublic void playVlc(String fileName) {System.out.println("Playing vlc file. Name: "+ fileName);        }@Overridepublic void playMp4(String fileName) {//do nothing}}public class Mp4Player implements AdvancedMediaPlayer{@Overridepublic void playVlc(String fileName) {//do nothing}@Overridepublic void playMp4(String fileName) {System.out.println("Playing mp4 file. Name: "+ fileName);        }}public class MediaAdapter implements MediaPlayer {AdvancedMediaPlayer advancedMusicPlayer;public MediaAdapter(String audioType){if(audioType.equalsIgnoreCase("vlc") ){advancedMusicPlayer = new VlcPlayer();            }else if (audioType.equalsIgnoreCase("mp4")){advancedMusicPlayer = new Mp4Player();}    }@Overridepublic void play(String audioType, String fileName) {if(audioType.equalsIgnoreCase("vlc")){advancedMusicPlayer.playVlc(fileName);}else if(audioType.equalsIgnoreCase("mp4")){advancedMusicPlayer.playMp4(fileName);}}}public class AdapterPatternDemo {public static void main(String[] args) {MediaPlayer audioPlayer = new AudioPlayer();audioPlayer.play("mp3", "beyond the horizon.mp3");MediaPlayer videoPlayer = new MediaAdapter();videoPlayer.play("vlc", "far far away.vlc");}
}

工厂模式 | Factory Design Pattern

终于到工厂模式了~~~~写了好久了呀QAQ

工厂模式是为了管理一个接口之下众多实现类。比如最常见的DAO接口。数据库中的表少至10个多可以至百个。在spring框架中,通过IOC和DI实现了这么多读取数据库的接口实现类的管理。那么在没有框架的场景下,如何才可以使上层代码和下层具体的DAO接口解耦呢?这时就需要工厂模式。通过工厂模式获得具体DAO接口。

至于为什么要选择这样的一个工厂模式,而不是直接new一个具体的实现类呢?这里举个例子。比方说,有一个DAO接口,实现该接口的有UserDaoImpl, AccountDaoImpl等。假设有两个类均用到UserDaoImpl。如果在这两个类中均使用new来创建一个新的UserDaoImpl,那么一旦有一天,因为需求变更,需要将UserDaoImpl换成AnotherUserDaoImpl,则需要在两个类中分别修改。那么如果有十个类,甚至一百个类都用到了这个Dao呢?这时候如果我是通过工厂来获得这个Dao,也就只需要在工厂中将返回值从原来的UserDaoImpl变成AnotherUserDaoImpl,并不会影响调用方。


简单工厂模式 | Static Factory Method

下面给一个简单的工厂模式的例子。

interface Dog
{public void speak ();
}class Poodle implements Dog
{public void speak(){System.out.println("The poodle says \"arf\"");}
}class Rottweiler implements Dog
{public void speak(){System.out.println("The Rottweiler says (in a very deep voice) \"WOOF!\"");}
}class SiberianHusky implements Dog
{public void speak(){System.out.println("The husky says \"Dude, what's up?\"");}
}class DogFactory
{public static Dog getDog(String criteria){if ( criteria.equals("small") )return new Poodle();else if ( criteria.equals("big") )return new Rottweiler();else if ( criteria.equals("working") )return new SiberianHusky();return null;}
}public class JavaFactoryPatternExample
{public static void main(String[] args){// create a small dogDog dog = DogFactory.getDog("small");dog.speak();// create a big dogdog = DogFactory.getDog("big");dog.speak();// create a working dogdog = DogFactory.getDog("working");dog.speak();}
}

在简单的工厂模式中,工厂根据输入的条件返回给一个接口的具体实现。
简单工厂模式有一个问题,就是一旦工厂出现新的产品,就必须修改工厂中获取产品的方法,这有违开闭原则。

而且工厂模式承担的压力过重,可能会导致职责的混乱。最重要的是,简单工厂模式中,获取产品的方法是静态方法,该方法无法通过继承等形式得到扩展。


工厂方法模式 | Factory Method Pattern

这其实是工厂模式的一个简单的升级。考虑一个真实工厂的场景。它的产品Product之下往往还有许多分类,如轴承,轮胎。各个子分类往往也对应着不同的车间,如轴承车间,轮胎车间。

如果还用简单工厂模式返回一个Product,且不说向上转型可能丢失的一些数据,而且工厂的压力也太大了,因为可能要根据不同场景返回上百个不同类型但继承了同一接口的类。这不符合设计原则。

这时候就出现了工厂方法模式。不仅仅对产品抽象,还对工厂抽象。对不同的产品提供不同的工厂,将职责进一步细化,满足SRP(单一职责原则)。同时,因为不需要输入无关的判断数据,也解除了控制耦合。

具体例子有最常见的日志系统。日志系统之下往往针对各个不同的子系统,比如数据库日志子系统,比如文件日志子系统。不同的日志系统对应的日志文件也不同。

这时通过工厂方法模式就可以很好的解决二者之间的关系。

在这张图中还可以继续延伸,比如数据库包括Mysql数据库,Oracle数据库等等。在UML图中也可以继续根据MySqlLog创建MysqlLogFactory。

还有一个具体的例子就是JAVA中的数据库连接。

Connection conn=DriverManager.getConnection("jdbc:microsoft:sqlserver://loc
alhost:1433; DatabaseName=DB;user=sa;password=");Statement statement=conn.createStatement();ResultSet rs=statement.executeQuery("select * from UserInfo");

这里通过DriverManager工厂根据输入的信息返回一个对应的连接。

连接中再返回对应的抽象语句statement。

根据工厂中的信息可以知道,这个statement的底层实现必定是一个类似SqlServerStatement的实现。

抽象工厂模式 | Abstract Factory Method

产品等级结构:产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。

产品族:在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中。

抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建 。当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、有效率。

在这里的上下文中,抽象工厂之下的子工厂被划分为海尔子工厂,海信子工厂。在海尔自工厂中可以获得海尔冰箱(productA),海尔电视机(productB), 同理,在海信子工厂中,可以获得海信冰箱(productA),海信电视机(productB)。

当然了 在大多数的应用场景下,工厂设计模式已经足够了。

对比1:适配器模式和抽象工厂模式的区别

工厂模式是负责加工的,适配器模式包括工厂模式,比工厂模式又高了一点点,增加了统一的抽象接口定义,方便以后在不同的数据库切换而不用改底层代码

对比2:适配器模式与装饰器模式的区别

适配器与装饰器模式的别名都是包装模式(Wrapper)。

区别

---适配器模式的意义

将一个接口转变成另一个接口,目的是通过改变接口来达到重复使用的目的。

----装饰器模式的意义

不改变被装饰对象的接口,而是保持原有的接口,增强原有对象的功能,或改变原有对象的处理方式而增提高性能。

设计模式1——策略模式 | 适配器模式 | 工厂模式相关推荐

  1. 设计模式-策略模式,观察者模式,装饰者模式,静态工厂模式,工厂方法模式

    设计模式这个东西,永远不是单单从书本上就能获取到的东西.曾经看到一个比喻,比喻的就很巧妙,文艺复兴时期的教学方式,那时候诞生了很多巨匠,达芬奇,莫开朗基罗,拉斐尔都是在这个时期绽放光芒的巨星.有一种说 ...

  2. 设计模式(1)--简单工厂模式、策略模式

    设计模式(1)--简单工厂模式.策略模式 1. 简单工厂模式 在阎宏博士的<JAVA与模式>一书中开头是这样描述简单工厂模式的:简单工厂模式是类的创建模式,又叫做静态工厂方法(Static ...

  3. 设计模式之适配器模式、委派模式、访问者模式、工厂模式、桥接模式(双维度扩展)

    设计模式之适配器模式.委派模式.访问者模式.工厂模式.观察者-发布订阅模式 设计模式分类: 适配器模式(Adapter Pattern) 定义 使用场景 代码实现 写法一:类适配器 写法二:对象适配器 ...

  4. 设计模式-策略模式和工厂模式结合使用

    怎么把策略模式和工厂模式结合起来使用 如果大家对策略模式和工厂模式不是很了解的话可以先看前面文章 策略模式:https://www.jianshu.com/p/958281936901 工厂模式:ht ...

  5. 设计模式(0)简单工厂模式

    0 设计模式基础 0.0 设计模式的定义 先来看一下设计模式常见的书面定义: 设计模式是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他 ...

  6. 策略模式、工厂模式、装饰者模式总结解析

    今天在面试的时候被问到自己策略模式怎么用的时候有被问懵到,以至于明明是自己的代码在脑海里已经混乱了,而且面试官提出的还是没有更好的利用设计模式也让我思考了一下我之前的代码到底是怎么实现的,重新梳理下策 ...

  7. 策略模式和工厂模式的区别

    文章目录 策略模式和工厂模式的区别 相似点 差异 用途不一样 关注点不一样 UML图 实例 策略模式和工厂模式的区别 相似点 在模式结构上,两者很相似: 差异 用途不一样 工厂是创建型模式,它的作用就 ...

  8. java策略模式和工厂模式的区别

    这两天初学设计模式的时候发现工厂模式和策略模式写法上相似,感觉很疑惑.既然相似,为何还要专门写成两种不同的模式呢.在翻阅了各个高手的总结后,偶有一点启发,特此记下. 有一个高手说 工厂相当于黑盒子,策 ...

  9. 单例模式,适配器模式,迭代器模式,工厂模式(C++实现)

    设计模式就相当于编程中的"孙子兵法",是经过很久的时间以及各路大神总结出来的多种实用,高效的业务设计中的套路; 单例模式 核心实现思想: 私有构造函数,拷贝构造,赋值重载; 一个类 ...

最新文章

  1. linux shell顺序执行,shell 执行顺序
  2. 1.2.4 计算机的工作过程(从源程序到可执行文件、指令执行过程的描述)
  3. boost::phoenix::arg_names::arg1用法的测试程序
  4. cf1132E. Knapsack(搜索)
  5. ad采样做按键开关_电池应用中的电流采样电阻设计
  6. 智慧北京02_初步ui框架_ 主界面_viewPager事件_xUtils_slidingMenu_网络缓存_数据传递...
  7. 【JSOI2008】最大数
  8. 【设计模式】第一章 面向对象六大原则
  9. r语言lm函数_如果R语言自带函数不能用,那我就自己写一个
  10. React Suite 组件库
  11. linux 计时程序,Linux下使用clock_gettime给程序计时
  12. Java基础面试题整理
  13. 论文阅读笔记:Weakly-supervised Semantic Segmentation in Cityscape via Hyperspectral Image
  14. 面向对象的三个基本特征
  15. 从0开始学Logisim,计算机硬件系统设计(1)
  16. 数据共享交换平台解决方案
  17. DOA定位算法源码程序
  18. 谈谈半个月 Goolge Baidu Live yahoo收录情况。
  19. 【MySQL】聚合函数
  20. 隐语义模型(LFM)-----------电影推荐算法

热门文章

  1. Berkeley DB 概述
  2. “看”车,有TA就够了!车辆分析工具PP-Vehicle开源上新!
  3. gopro6 连接电脑_如何将外部麦克风连接到GoPro
  4. django 单元测试_如何将单元测试添加到Django项目
  5. 攻防演练结束后 全面复盘,总结经验
  6. 中国高智商协会(胜寒) 入会智商考试热身题答案
  7. 孙正义:30年后AI的智商将达到10000,你与机器人之间的智力将差49个半爱恩斯坦
  8. 用自己的mqtt服务器实现家居控制和监测
  9. java项目开发大鱼吃小鱼只需两小时
  10. android动画sin cos,高中数学知识点:sin和cos运用到三角形上(动画版)