设计模式在实际场景中的使用

      设计模式一、  什么是设计模式?

设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。
使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。

二、  设计模式的分类

总体来说设计模式分为三大类:
l  创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
l  结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
l  行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

三、  设计模式的六大原则1、      开闭原则(Open Close Principle)

开闭原则就是说 对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。

2、     里氏代换原则(LiskovSubstitution Principle)

里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。—— From Baidu 百科

3、     依赖倒转原则(DependenceInversion Principle)

这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。

4、     接口隔离原则(InterfaceSegregation Principle)

这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。

5、     迪米特法则(最少知道原则)(Demeter Principle)

为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。

6、     合成复用原则(CompositeReuse Principle)

原则是尽量使用合成/聚合的方式,而不是使用继承。
 

四、  常用的设计模式1   单例模式(singleton)

单例对象(Singleton)是一种常用的设计模式。在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在。这样的模式有几个好处:
l  在内存中只有一个对象,节省内存空间。
l  避免频繁的创建销毁对象,可以提高性能。
l  避免对共享资源的多重占用。
l  可以全局访问
适用场景:
l  需要频繁实例化然后销毁的对象。
l  创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
l  有状态的工具类对象。
l  频繁访问数据库或文件的对象。
l  以及其他要求只有一个对象的场景
 

2   工厂方法模式(Factory Method)

概述:定义一个用于创建对象的接口,让子类决定实例化哪一个类。
FactoryMethod使一个类的实例化延迟到其子类。
适用性:
1.当一个类不知道它所必须创建的对象的类的时候。
 
2.当一个类希望由它的子类来指定它所创建的对象的时候。
3.当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。
相关代码:
interface IProduct {  
   public void productMethod();  
}  
  
class Product implements IProduct {  
   public void productMethod() {  
       System.out.println("产品");  
   }  
}  
  
interface IFactory {  
   public IProduct createProduct();  
}  
  
class Factory implements IFactory {  
   public IProduct createProduct() {
       return new Product();  
   }  
}  
  
public class Client {  
   public static void main(String[] args) {
       IFactory factory = new Factory();
       IProduct prodect = factory.createProduct();  
       prodect.productMethod();  
   }  
}

3   抽象工厂模式(Abstract Factory)

抽象工厂模式是工厂方法模式的升级版本,他用来创建一组相关或者相互依赖的对象。他与工厂方法模式的区别就在于,工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则是针对的多个产品等级结构。在编程中,通常一个产品结构,表现为一个接口或者抽象类,也就是说,工厂方法模式提供的所有产品都是衍生自同一个接口或抽象类,而抽象工厂模式所提供的产品则是衍生自不同的接口或抽象类
相关代码:
interface IProduct1 {  
   public void show();  
}  
interface IProduct2 {  
   public void show();  
}  
  
class Product1 implements IProduct1 {  
   public void show() {  
       System.out.println("这是1型产品");  
   }  
}  
class Product2 implements IProduct2 {  
   public void show() {  
       System.out.println("这是2型产品");  
   }  
}  
  
interface IFactory {  
   public IProduct1 createProduct1();
   public IProduct2 createProduct2();
}  
class Factory implements IFactory{  
   public IProduct1 createProduct1() {
       return new Product1();  
   }  
   public IProduct2 createProduct2() {
       return new Product2();  
   }  
}  
  
public class Client {  
   public static void main(String[] args){
       IFactory factory = new Factory();
       factory.createProduct1().show();  
       factory.createProduct2().show();  
   }  
}  

4   代理模式(Proxy)

理模式是一种应用非常广泛的设计模式,当客户端代码需要调用某个对象时,客户端实际上不关心是否准确得到该对象,它只要一个能提供该功能的对象即可,此时我们就可返回该对象的代理(Proxy)
主要优点:
(1).职责清晰
真实的角色就是实现实际的 业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件完成事务,附带的结果就是编程简洁清晰。
(2).代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了中介的作用和保护了目标对象的作用。
(3).高扩展性
 

5   命令模式(Command)

将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。
类图:
file:///D:/Users/YUANMI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image001.jpg
命令模式的结构
        顾名思义,命令模式就是对命令的封装,首先来看一下命令模式类图中的基本结构:
Command类:是一个抽象类,类中对需要执行的命令进行声明,一般来说要对外公布一个execute方法用来执行命令。
ConcreteCommand类:Command类的实现类,对抽象类中声明的方法进行实现。
Client类:最终的客户端调用类。
        以上三个类的作用应该是比较好理解的,下面我们重点说一下Invoker类和Recevier类。
Invoker类:调用者,负责调用命令。
Receiver类:接收者,负责接收命令并且执行命令。
        所谓对命令的封装,说白了,无非就是把一系列的操作写到一个方法中,然后供客户端调用就行了,反映到类图上,只需要一个ConcreteCommand类和Client类就可以完成对命令的封装,即使再进一步,为了增加灵活性,可以再增加一个Command类进行适当地抽象,这个调用者和接收者到底是什么作用呢?
       命令模式作为一种行为类模式,首先要做到低耦合,耦合度低了才能提高灵活性,而加入调用者和接收者两个角色的目的也正是为此。命令模式的通用代码如下:
class Invoker {  
   private Command;  
   public void setCommand(Command command) {  
       this.command = command;  
   }  
   public void action(){  
       this.command.execute();  
   }  
}  
  
abstract class Command {  
   public abstract void execute();  
}  
  
class ConcreteCommand extends Command{  
   private Receiver;  
   public ConcreteCommand(Receiver receiver){  
       this.receiver = receiver;  
   }  
   public void execute() {  
       this.receiver.doSomething();  
   }  
}  
  
class Receiver {  
   public void doSomething(){  
        System.out.println("接受者-业务逻辑处理");  
   }  
}  
  
public class Client {  
   public static void main(String[] args){
       Receiver = new Receiver();  
       Command = new ConcreteCommand(receiver);
       //客户端直接执行具体命令方式(此方式与类图相符)  
       command.execute();  
  
       //客户端通过调用者来执行命令
       Invoker = new Invoker();  
       invoker.setCommand(command);  
       invoker.action();  
   }  
}

6   策略模式(Strategy)

定义一组 算法,将每个算法都封装起来,并且使他们之间可以互换。
行为类模式
类图:
file:///D:/Users/YUANMI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg
       策略模式是对算法的封装,把一系列的算法分别封装到对应的类中,并且这些类实现相同的接口,相互之间可以替换。
如上图:
l   封装类:也叫上下文,对策略进行二次封装,目的是避免高层模块对策略的直接调用。
l   抽象策略:通常情况下为一个接口,当各个实现类中存在着重复的逻辑时,则使用抽象类来封装这部分公共的代码,此时,策略模式看上去更像是模版方法模式。
l   具体策略:具体策略角色通常由一组封装了算法的类来担任,这些类之间可以根据需要自由替换。
实例代码:
interface IStrategy {  
   public void doSomething();  
}  
class ConcreteStrategy1 implementsIStrategy {  
   public void doSomething() {  
       System.out.println("具体策略1");  
   }  
}  
class ConcreteStrategy2 implementsIStrategy {  
   public void doSomething() {  
       System.out.println("具体策略2");  
   }  
}  
class Context {  
   private IStrategy strategy;  
     
   public Context(IStrategy strategy){
       this.strategy = strategy;  
   }  
     
   public void execute(){  
       strategy.doSomething();  
   }  
}  
  
public class Client {  
   public static void main(String[] args){
       Context;  
       System.out.println("-----执行策略1-----");  
       context = new Context(new ConcreteStrategy1());  
       context.execute();  
  
       System.out.println("-----执行策略2-----");  
       context = new Context(new ConcreteStrategy2());  
       context.execute();  
   }  
}

7    外观模式(Facade)

随着系统的不断改进和开发,它们会变得越来越复杂,系统会生成大量的类,这使得程序流程更难被理解。门面模式可为这些类提供一个简化的接口,从而简化访问这些类的复杂性。
       外观模式(Facade)也被称为正面模式、门面模式,这种模式用于将一组复杂的类包装到一个简单的外部接口中。
实例代码:
Public class Facade {
    // 定义被Facade封装的三个部门
    Payment pay;
    Cook cook;
    Waiter waiter;
 
    // 构造器
    public Facade(){
       this.pay = new PaymentImpl();
       this.cook = new CookImpl();
       this.waiter = new WaiterImpl();
    }
 
    Public void serveFood(){
       // 依次调用三个部门的方法,封装成一个serveFood()方法
       Stringfood = pay.pay();
       food= cook.cook(food);
       waiter.serve(food);
    }
}
Main方法直接如下调用:
Facadef = new Facade();
f.serveFood();
 
 

8   桥接模式(Bridge)

由于实际的需要,某个类具有两个以上的维度变化,如果只是使用继承将无法实现这种需要,或者使得设计变得相当臃肿。而桥接模式的做法是把变化部分抽象出来,使变化部分与主类分离开来,从而将多个的变化彻底分离。最后提供一个管理类来组合不同维度上的变化,通过这种组合来满足业务的需要。
实例代码:
Peppery口味风格接口:
Public interface Peppery
{
    String style();
}
口味之一
Public class PepperySytle implements Peppery
{
    //实现"辣味"风格的方法
    public Stringstyle()
    {
       return"辣味很重,很过瘾...";
    }
}
口味之二
Public class PlainStyle implements Peppery
{
    //实现"不辣"风格的方法
    public Stringstyle()
    {
       return"味道清淡,很养胃...";
    }
}
口味的桥梁
Public abstractclass AbstractNoodle
{
    //组合一个Peppery变量,用于将该维度的变化独立出来
    protected Peppery style;
    //每份Noodle必须组合一个Peppery对象
    public AbstractNoodle(Pepperystyle)
    {
       this.style =style;
    }
    Public abstractvoid eat();
}
材料之一,继承口味
Public class PorkyNoodle extends AbstractNoodle
{
    public PorkyNoodle(Pepperystyle)
    {
       super(style);
    }
    //实现eat()抽象方法
    publicvoid eat()
    {
       System.out.println("这是一碗稍嫌油腻的猪肉面条。"
           + super.style.style());
    }
}
材料之二,继承口味
Public class BeefMoodle extends AbstractNoodle
{
    public BeefMoodle(Pepperystyle)
    {
       super(style);
    }
    //实现eat()抽象方法
    publicvoid eat()
    {
       System.out.println("这是一碗美味的牛肉面条。"
           + super.style.style());
    }
}
主程序
Public cclass Test
{
    publicstaticvoid main(String[]args)
    {
       //下面将得到“辣味”的牛肉面
       AbstractNoodlenoodle1 = new BeefMoodle(
           new PepperySytle());
       noodle1.eat();
       //下面将得到“不辣”的牛肉面
       AbstractNoodlenoodle2 = new BeefMoodle(
           new PlainStyle());
       noodle2.eat();
       //下面将得到“辣味”的猪肉面
       AbstractNoodlenoodle3 = new PorkyNoodle(
           new PepperySytle());
       noodle3.eat();
       //下面将得到“不辣”的猪肉面
       AbstractNoodlenoodle4 = new PorkyNoodle(
           new PlainStyle());
       noodle4.eat();
    }
}

9    观察者模式(Observer)

定义对象间一种一对多的依赖关系,使得当每一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。
类图:
file:///D:/Users/YUANMI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image003.jpg
观察者模式的结构
在最基础的观察者模式中,包括以下四个角色:
l 被观察者:从类图中可以看到,类中有一个用来存放观察者对象的Vector容器(之所以使用Vector而不使用List,是因为多线程操作时,Vector在是安全的,而List则是不安全的),这个Vector容器是被观察者类的核心,另外还有三个方法:attach方法是向这个容器中添加观察者对象;detach方法是从容器中移除观察者对象;notify方法是依次调用观察者对象的对应方法。这个角色可以是接口,也可以是抽象类或者具体的类,因为很多情况下会与其他的模式混用,所以使用抽象类的情况比较多。
l 观察者:观察者角色一般是一个接口,它只有一个update方法,在被观察者状态发生变化时,这个方法就会被触发调用。
l  
l  具体的被观察者:使用这个角色是为了便于扩展,可以在此角色中定义具体的业务逻辑。
l 具体的观察者:观察者接口的具体实现,在这个角色中,将定义被观察者对象状态发生变化时所要处理的逻辑。
实例代码:
abstract class Subject {  
   private Vector<Observer> obs = new Vector<Observer>();  
     
   public void addObserver(Observer obs){
       this.obs.add(obs);  
   }  
   public void delObserver(Observer obs){
       this.obs.remove(obs);  
   }  
   protected void notifyObserver(){  
       for(Observer o: obs){  
           o.update();  
       }  
   }  
   public abstract void doSomething();
}  
  
class ConcreteSubject extends Subject{  
   public void doSomething(){  
       System.out.println("被观察者事件反生");  
       this.notifyObserver();  
   }  
}  
interface Observer {  
   public void update();  
}  
class ConcreteObserver1 implements Observer{  
   public void update() {  
       System.out.println("观察者1收到信息,并进行处理。");  
   }  
}  
class ConcreteObserver2 implements Observer{  
   public void update() {  
       System.out.println("观察者2收到信息,并进行处理。");  
   }  
}  
  
public class Client {  
   public static void main(String[] args){
       Subject sub = new ConcreteSubject();
       sub.addObserver(new ConcreteObserver1()); //添加观察者1  
       sub.addObserver(new ConcreteObserver2()); //添加观察者2  
       sub.doSomething();  
   }  
}
 
适用场景:
当一个对象的状态发生改变,某些与它相关的对象也要随之做出相应的变化。比如,我们要设计一个右键菜单的功能,只要在软件的有效区域内点击鼠标右键,就会弹出一个菜单;再比如,我们要设计一个自动部署的功能,就像eclipse开发时,只要修改了文件,eclipse就会自动将修改的文件部署到服务器中。这两个功能有一个相似的地方,那就是一个对象要时刻监听着另一个对象,只要它的状态一发生改变,自己随之要做出相应的行动。其实,能够实现这一点的方案很多,但是,无疑使用观察者模式是一个主流的选择
 

10 解释器模式(Interpreter)

给定一种语言,定义他的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中句子。
类图:
如上图:
l 抽象解释器:声明一个所有具体表达式都要实现的抽象接口(或者抽象类),接口中主要是一个interpret()方法,称为解释操作。具体解释任务由它的各个实现类来完成,具体的解释器分别由终结符解释器TerminalExpression和非终结符解释器NonterminalExpression完成。
l 终结符表达式:实现与文法中的元素相关联的解释操作,通常一个解释器模式中只有一个终结符表达式,但有多个实例,对应不同的终结符。终结符一半是文法中的运算单元,比如有一个简单的公式R=R1+R2,在里面R1和R2就是终结符,对应的解析R1和R2的解释器就是终结符表达式。                                
l 非终结符表达式:文法中的每条规则对应于一个非终结符表达式,非终结符表达式一般是文法中的运算符或者其他关键字,比如公式R=R1+R2中,+就是非终结符,解析+的解释器就是一个非终结符表达式。非终结符表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式。
l 环境角色:这个角色的任务一般是用来存放文法中各个终结符所对应的具体值,比如R=R1+R2,我们给R1赋值100,给R2赋值200。这些信息需要存放到环境角色中,很多情况下我们使用Map来充当环境角色就足够了。
实例代码:
class Context {}  
abstract class Expression {  
   public abstract Object interpreter(Context ctx);  
}  
class TerminalExpression extends Expression{  
   public Object interpreter(Context ctx){
       return null;  
   }  
}  
class NonterminalExpression extendsExpression {  
   public NonterminalExpression(Expression...expressions){  
         
   }  
   public Object interpreter(Context ctx){
       return null;  
   }  
}  
public class Client {  
   public static void main(String[] args){
       String expression = "";
       char[] charArray = expression.toCharArray();  
       Context ctx = new Context();  
       Stack<Expression> stack = new Stack<Expression>();  
       for(int i=0;i<charArray.length;i++){
           //进行语法判断,递归调用
       }  
       Expression exp = stack.pop();  
       exp.interpreter(ctx);  
   }  
}
适用场景:
l 有一个简单的语法规则,比如一个sql语句,如果我们需要根据sql语句进行转换,就可以使用解释器模式来对语句进行解释。
l 一些重复发生的问题,比如加减乘除四则运算,但是公式每次都不同,有时是a+b-c*d,有时是a*b+c-d,等等等等个,公式千变万化,但是都是由加减乘除四个非终结符来连接的,这时我们就可以使用解释器模式。
  • 5
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值