设计模式的一些故事.docx
《设计模式的一些故事.docx》由会员分享,可在线阅读,更多相关《设计模式的一些故事.docx(38页珍藏版)》请在冰豆网上搜索。
设计模式的一些故事
设计模式的一些故事
前言
为何要了解设计模式,所谓设计模式,就是一些“标准”的代码编写方法和格式。
学习的好处:
你能够更快看懂开源的代码理解作者的思想;你自己编写的代码更规范,更显专业。
设计模式无关语言开发工具,对于所有计算机代码都是通用的,因为它本身不是源于计算机,而是出自建筑行业。
(有兴趣可查看GOF相关书籍)
如果你的代码不遵循这些通用的模式或标准来编写,就无法很好遵循软件基本的“开放-闭合”原则,整个程序其实就是个大型的switch语句集合,可读性差而且在不同情况下调用不同的函数——这导致了代码的紧耦合以及整个软件难以适应变化。
整个程序就像真正用卡片做成的房子一样,几乎每天都会轰然倒下。
即使是最微小的变化可能都要花费几个小时来恢复程序的稳定性。
如果你能够理解设计模式并很好的使用,工作中就可能尽量避免弯路,毕竟这些都是前人从生活中总结的经验教训。
一、单例Singleton模式
来看一下使用场景:
一个国家只能有一位总统(可能在正常情况下)。
所以不管任何时候我们需要一位总统,使用AmericaPresident就能返回一个。
getPresident()方法将确保只有一个总统对象被创建。
否者,一个国家有多个总统就不妙了。
单例模式Java示例代码
packagecom.programcreek.designpatterns.singleton;
publicclassAmericaPresident{
privateAmericaPresident(){ }
privatestaticfinalAmericaPresidentthePresident=newAmericaPresident();
publicstaticAmericaPresidentgetPresident(){
returnthePresident;
}
}
模式升级,在第一次使用的时候创建单例。
privatestaticAmericaPresidentthePresident=null;
publicstaticAmericaPresidentgetPresident(){
if(thePresident==null){
thePresident=newAmericaPresident();
}
returnthePresident;
}
单例模式在 Java 标准库中的使用
java.lang.Runtime#getRuntime() 是Java 标准库中常用的方法,它返回与当前Java应用关联的运行时对象。
下面是getRunTime() 的一个简单例子,它在windows系统上读取一个网页。
Processp=Runtime.getRuntime().exec(
"C:
/windows/system32/ping.exe");
//getprocessinputstreamandputittobufferedreader
BufferedReaderinput=newBufferedReader(newInputStreamReader(
p.getInputStream()));
Stringline;
while((line=input.readLine())!
=null){
System.out.println(line);
}
input.close();
上述代码就是调用操作系统的ping命令。
二、工厂Factory模式
1.关于工厂模式
工厂模式是根据不同的参数创建对象。
例如用工厂创建人。
如果我们想要一个男孩,工厂就会为我们生产一个男孩(BoyClass);如果我们需要一个女孩(GirlClass),工厂则会为我们生产一个女孩。
工厂会根据不同的参数,为我们提供不同的物品。
2.工厂模式类图
3.工厂模式Java代码
interfaceHuman{
publicvoidTalk();
publicvoidWalk();
}
classBoyimplementsHuman{
@Override
publicvoidTalk(){
System.out.println("Boyistalking...");
}
@Override
publicvoidWalk(){
System.out.println("Boyiswalking...");
}
}
classGirlimplementsHuman{
@Override
publicvoidTalk(){
System.out.println("Girlistalking...");
}
@Override
publicvoidWalk(){
System.out.println("Girliswalking...");
}
}
publicclassHumanFactory{
publicstaticHumancreateHuman(Stringm){
Humanp=null;//定义基类,根据不同的类型来创建子类对象。
if(m=="boy"){
p=newBoy();
}elseif(m=="girl"){
p=newGirl();
}
returnp;
}
}
调用HumanFactory.createHuman方法按需创建男孩或女孩对象。
4.工厂模式在Java标准库中的应用
根据不同的参数,getInstance()方法会返回不同的Calendar(日历)对象。
java.util.Calendar–getInstance()
java.util.Calendar–getInstance(TimeZonezone)
java.util.Calendar–getInstance(LocaleaLocale)
java.util.Calendar–getInstance(TimeZonezone,LocaleaLocale)
java.text.NumberFormat–getInstance()
java.text.NumberFormat–getInstance(LocaleinLocale)
三、抽象工厂AbstractFactory模式
抽象工厂模式是在工厂模式的基础上增加的一层抽象概念。
如果比较抽象工厂模式和工厂模式,我们不难发现前者只是增加了一层抽象的概念。
抽象工厂是一个父类工厂,可以创建其它工厂类。
故我们也叫它“工厂的工厂”。
1、抽象工厂类图
2、抽象工厂Java示例代码
//CPU产品的接口。
interfaceCPU{
voidprocess();
}
//能够生产CPU工厂的基类
interfaceCPUFactory{
CPUproduceCPU();
}
classAMDFactoryimplementsCPUFactory{
publicCPUproduceCPU(){
returnnewAMDCPU();
}
}
classIntelFactoryimplementsCPUFactory{
publicCPUproduceCPU(){
returnnewIntelCPU();
}
}
classAMDCPUimplementsCPU{
publicvoidprocess(){
System.out.println("AMDisprocessing...");
}
}
classIntelCPUimplementsCPU{
publicvoidprocess(){
System.out.println("Intelisprocessing...");
}
}
classComputer{
CPUcpu;
publicComputer(CPUFactoryfactory){
cpu=factory.produceCPU();
cpu.process();
}
}
publicclassClient{
publicstaticvoidmain(String[]args){
newComputer(createSpecificFactory());
}
publicstaticCPUFactorycreateSpecificFactory(){
intsys=0;//基于特定要求
if(sys==0)
returnnewAMDFactory();
else
returnnewIntelFactory();
}
}
在当今的架构中,抽象工厂是一个非常重要的概念。
越来越复杂的功能模块,需要各种工厂来维护创建和管理。
四、建造者Builder模式
建造者模式的关键特性是它将一个建造过程分解成很多步骤,也可以说,每个产品的建造会遵循同样的流程,不过流程内的每一个步骤都不尽相同。
就像工厂的流水线制造加工一样。
在下面这个故事里,我们会定义一个叫作星巴克饮料机(StarbucksBuilder)的机器,用于制造星巴克饮料。
StarbucksBuilder要经过很多步骤来做一杯饮料,比如buildSize()和buildDrink(),并且最终返回制成的饮料。
(对于代码而言,每一个步骤就是一个独立的方法)
1、建造者设计模式类图
主要类说明:
Starbucks:
星巴克饮品类
StarbucksBuilder:
星巴克饮品的创建类
CoffeeBuilder:
咖啡类饮品的创建类,继承自StarbucksBuilder
TeaBuilder:
茶类饮品的创建类,继承自StarbucksBuilder
Waiter:
为客户提供饮品的侍者类
Customer:
购买饮品的客户类
2、建造者模式Java示例代码
packagedesignpatterns.builder;
//待构建产品
classStarbucks{
privateStringsize;
privateStringdrink;
publicvoidsetSize(Stringsize){
this.size=size;
}
publicvoidsetDrink(Stringdrink){
this.drink=drink;
}
}
//抽象builder(类比抽象工厂)
abstractclassStarbucksBuilder{
protectedStarbucksstarbucks;
publicStarbucksgetStarbucks(){
returnstarbucks;
}
publicvoidcreateStarbucks(){
starbucks=newStarbucks();
System.out.println("adrinkiscreated");
}
publicabstractvoidbuildSize();//抽象方法,在子类中实现。
publicabstractvoidbuildDrink();
}
//茶类饮品的builder
classTeaBuilderextendsStarbucksBuilder{
publicvoidbuildSize(){
starbucks.setSize("large");
System.out.println("buildlargesize");
}
publicvoidbuildDrink(){
starbucks.setDrink("tea");
System.out.println("buildtea");
}
}
//咖啡类饮品builder
classCoffeeBuilderextendsStarbucksBuilder{
publicvoidbuildSize(){
starbucks.setSize("medium");
System.out.println("buildmediumsize");
}
publicvoidbuildDrink(){
starbucks.setDrink("coffee");
System.out.println("buildcoffee");
}
}
//指导如何封装builder
classWaiter{
privateStarbucksBuilderstarbucksBuilder;
//接受指令,要准备什么类型的饮品。
publicvoidsetStarbucksBuilder(StarbucksBuilderbuilder){
starbucksBuilder=builder;
}
//创建相应的饮品实例
publicStarbucksgetstarbucksDrink(){
returnstarbucksBuilder.getStarbucks();
}
//处理客户的请求
publicvoidconstructStarbucks(){
starbucksBuilder.createStarbucks();
starbucksBuilder.buildDrink();
starbucksBuilder.buildSize();
}
}
//客户类
publicclassCustomer{
//主函数在此。
publicstaticvoidmain(String[]args){
Waiterwaiter=newWaiter();
StarbucksBuildercoffeeBuilder=newCoffeeBuilder();
//也可以用泡茶builder沏茶
//StarbucksBuilderteaBuilder=newTeaBuilder();
waiter.setStarbucksBuilder(coffeeBuilder);//提供一个咖啡的请求
waiter.constructStarbucks();
//取到饮料
Starbucksdrink=waiter.getstarbucksDrink();
}
}
3、建造者模式的实际用法
很多类库都有使用建造者模式,最经典的莫过于字符串拼接:
StringBuilderstrBuilder=newStringBuilder();
strBuilder.append("one");
strBuilder.append("two");
strBuilder.append("three");
Stringstr=strBuilder.toString();
append()方法类似于星巴克例子中的一个步骤,toString()方法是其中的最后一步。
从这幅图,我们很容易理解String为何是不可变的。
系统全貌比较复杂,不过继承自AbstractStringBuilder的StringBuilder,正是上面所说的建造者模式。
4、建造者模式和工厂模式的区别
当创造一个对象需要很多步骤时适合使用建造者模式。
而当只需调用一个方法就可以简单地创建整个对象时适合使用工厂模式。
建造者可以理解为“带有详细生产步骤的工厂”。
五、原型Prototype模式
原型模式被用在频繁调用且极其相似的对象上,它会克隆对象并设置改变后的属性,而且消耗的资源较少。
1、原型模式类图
2、原型模式Java示例代码
packagedesignpatterns.prototype;
//原型
interfacePrototype{
voidsetSize(intx);
voidprintSize();
}
//一个具体类
classAimplementsPrototype,Cloneable{
privateintsize;
publicA(intx){
this.size=x;
}
@Override
publicvoidsetSize(intx){
this.size=x;
}
@Override
publicvoidprintSize(){
System.out.println("Size:
"+size);
}
@Override
publicAclone()throwsCloneNotSupportedException{
return(A)super.clone();//使用基类的浅拷贝方法。
}
}
//需要很多类似的对象进行测试
publicclassPrototypeTest{
publicstaticvoidmain(Stringargs[])throwsCloneNotSupportedException{
Aa=newA
(1);
for(inti=2;i<10;i++){
Prototypetemp=a.clone();
temp.setSize(i);
temp.printSize();
}
}
}
3、原型模式在Java标准函数库中的使用
java.lang.Object–clone()
六、观察者Observer模式
在界面交互开发中,观察者模式被用来对GUI中的动作做侦听。
SwingGUI的例子就表明了动作侦听是怎样实现观察者模式的。
下面是一个猎头的典型例子。
这个图中有2个角色-猎头和求职者。
求职者先在猎头处注册,当有新的工作机会时猎头就会通知求职者。
1、观察者模式类图
2、Java示例代码
Subject接口
publicinterfaceSubject{
publicvoidregisterObserver(Observero);//注册一个观察者
publicvoidremoveObserver(Observero);//移除一个观察者
publicvoidnotifyAllObservers();//通知所有的观察者
}
Observer接口
publicinterfaceObserver{
publicvoidupdate(Subjects);//观察者接收消息后的反馈。
}
Hunter类实现了Subject接口
importjava.util.ArrayList;
publicclassHeadHunterimplementsSubject{
//definealistofusers,suchasMike,Bill,etc.
privateArrayListuserList;
//definealistofjobs,suchasPG,DS,TE,etc
privateArrayListjobs;
publicHeadHunter(){
userList=newArrayList();
jobs=newArrayList();
}
@Override
publicvoidregisterObserver(Observero){
userList.add(o);
}
@Override
publicvoidremoveObserver(Observero){}
@Override
publicvoidnotifyAllObservers(){
for(Observero:
userList){
o.update(this);
}
}
publicvoidaddJob(Stringjob){
this.jobs.add(job);
notifyAllObservers();//有新的工作,就通知所有观察者