澳门新葡亰平台游戏网站JAVA设计模式-HeadFirst讲解

澳门新葡亰平台游戏网站 2

1984年,我以机械工程学位从大学毕业,开始了软件工程师的职业生涯。自学C语言之后,1985年从事了用于Unix的50,000行用户图形界面(GUI)开发。整个过程非常轻松愉快。

一、设计原则

模式是在某情景下,针对某问题的某种解决方案。模式必然应用于一个重复出现的问题。问题包括了一个目标和一组约束。模式是被“发现”的,而不是被“创建”的。良好的设计模式必须具备可复用、可扩充、可维护三个特性。

1985年底,我的编码工作完成了,之后我考虑开始其他的项目——或者说我认为我可以进行新的项目了。但很快我收到了一系列bug报告和新增的需求,为修正错误我开始努力阅读这50,000行代码。这个工作却非常艰难。

  1. 封装变化
  2. 多用组合,少用继承
  3. 针对接口编程,不针对实现编程
  4. 为交互对象之间的松紧耦合设计而努力
  5. 对扩展开放,都修稿关闭
  6. 依赖抽象,不要依赖具体类
  7. 最少知识原则:之和朋友交谈
  8. 好莱坞原则:别找我,我会找你(由超类主控一切,当他们需要的时候,自然回去调用子类)
  9. 类应该只有一个改变的理由

设计模式分为创建型、行为型和结构性。

整个程序就像真正用卡片做成的房子一样,几乎每天都会轰然倒下。即使是最微小的变化我也要花费几个小时来恢复程序的稳定性。

二、设计模式

  1. 创建型涉及到将对象实例化,这类模式都提供一个方法,将客户从所需要实例化的对象中解耦。包括:工厂模式,抽象工厂模式,单例模式,生成器模式,原型模式。

  2. 行为型都涉及到类和对象如何交互及分配职责。包括:模版模式,命令模式,迭代器模式,观察者模式,状态模式,策略模式,访问者模式,中介者模式
    ,备忘录模式,责任链模式,翻译者模式。

  3. 结构型可以让你把类或对象组合到更大的结构中。用来描述类和对象如何组合以建立新的结构或新的功能。包括:装饰者模式,组合模式,适配器模式,代理模式,外观模式,桥接模式,蝇量模式,

可能我碰巧发现了一个重要的软件工程原则:开发阶段轻松愉快,然后项目部署后就去找下一份工作。然而,实际上我的困难源于我对面向对象(object-oriented,OO)软件开发基本原则——封装的无知。我的程序就是个大型的switch语句集合,在不同情况下调用不同的函数——这导致了代码的紧耦合以及整个软件难以适应变化。

  1. 策略模式

另一种分类方法将设计模式分为:处理类的模式,处理对象的模式。

在Java设计模式这篇文章,我会讨论策略模式,它可能是最基础的设计模式吧。如果在1984年的时候我知道策略模式的话,有很大一部分工作就可以避免了。

         
定义算法族,分别封装起来,让他们之间可以互相替换,次模式让算法的变化独立于使用算法的客户

  1. 类模板模式,适配器模式,工厂模式,翻译者模式

  2. 对象装饰者模式,组合模式,代理模式,外观模式,桥接模式,蝇量模式,命令模式,观察者模式,状态模式,策略模式,访问者模式,中介者模式
    ,备忘录模式,责任链模式,抽象工厂模式,单例模式,生成器模式,原型模式。

策略模式

在GOF的设计模式一书的第一章,作者讨论了若干条OO设计原则,这些原则包括了很多设计模式的核心。策略模式体现了这样两个原则——封装变化对接口编程而不是对实现编程设计模式的作者把策略模式定义如下:

Define a family of algorithms, encapsulate each one, and make them
interchangeable. [The] Strategy [pattern] lets the algorithm vary
independently from clients that use
it.(策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而变化。)

策略模式将整个软件构建为可互换部分的松耦合的集合,而不是单一的紧耦合系统。松耦合的软件可扩展性更好,更易于维护且重用性好。

为理解策略模式,我们首先看一下Swing如何使用策略模式绘制组件周围的边框。接着讨论Swing使用策略模式带来的好处,最后说明在你的软件中如何实现策略模式。

 

Swing 边框

几乎所有的Swing组件都可以绘制边框,包括面板、按钮、列表等等。Swing也提供了组件的多种边框类型:bevel(斜面边框),etched(浮雕化边框),line(线边框),titled(标题边框)以及compound(复合边框)等。Swing组件的边框使用JComponent类绘制,它是所有Swing组件的基类,实现了所有Swing组件的常用功能。

JComponent实现了paintBorder(),该方法用来绘制组件周围的边框。假如Swing的创建者使用类似示例1的方法实现paintBorder():

// A hypothetical JComponent.paintBorder method
protected void paintBorder(Graphics g) {
   switch(getBorderType()) {
      case LINE_BORDER:   paintLineBorder(g);
                          break;
      case ETCHED_BORDER: paintEtchedBorder(g);
                          break;
      case TITLED_BORDER: paintTitledBorder(g);
                          break;
      ...
   }
}

示例1 绘制Swing边框的错误方式

示例1中JComponent.paintBorder()方法在JComponent硬编码了边框的绘制。

如果你想实现一种新的边框类型,可以想见这样的结果——需要修改JComponent类的至少三个地方:首先,添加与新边框类型相关的新的整数值。第二,switch语句中添加case语句。第三,实现paintXXXBorder()方法,XXX表示边框类型。

很显然,扩展前面的paintBorder()吃力不讨好。你会发现不仅paintBorder()很难扩展新类型,而且JComponent类不是你首先要修改的位置,它是Swing工具包的一部分,这意味着你将不得不重新编译类和重建全部工具包。你也必须要求你的用户使用你自己的Swing版本而不是标准版,Swing下一次发布后这些工作依然要做。此外,因为你为JComponent类添加了新的边框绘制功能,无论你是否喜欢每个Swing组件都可以访问该功能的现状——你不能把你的新边框限制到具体的组件类型。

可见,如果JComponent类使用示例1中的switch语句实现其功能,Swing组件就不能被扩展。

那么运用OO思想如何实现呢?使用策略模式解耦JComponent与边框绘制的代码,这样无需修改JComponent类就实现了边框绘制算法的多样性。使用策略模式封装变化,即绘制边框方法的变化,以及对接口编程而不是对实现编程,提供一个Border接口。接下来就看看JComponent如何使用策略模式绘制边框。示例2为JComponent.paintBorder()方法:

// The actual implementation of the JComponent.paintBorder() method
protected void paintBorder(Graphics g) {
   Border border = getBorder();
   if (border != null) {
      border.paintBorder(this, g, 0, 0, getWidth(), getHeight());
   }
}

示例2 绘制Swing边框的正确方式

前面的paintBorder()方法绘制了有边框物体的边框。在这种情况下,边框对象封装了边框绘制算法,而不是JComponent类。

注意JComponent把自身的引用传递给Border.paintBorder(),这样边框对象就可以从组件获取信息,这种方式通常称为委托。通过传递自身的引用,一个对象将功能委托给另一对象。

JComponent类引用了边框对象,作为JComponent.getBorder()方法的返回值,示例3为相关的setter方法。

...
private Border border;
...
public void setBorder(Border border) {
   Border oldBorder = this.border;
   this.border = border;
   firePropertyChange("border", oldBorder, border);
   if (border != oldBorder) {
      if (border == null || oldBorder == null || !(border.getBorderInsets(this).
                                    equals(oldBorder.getBorderInsets(this)))) {
         revalidate();
      }       
      repaint();
   }
}
...
public Border getBorder() {
   return border;
}

示例3 Swing组件边框的setter和getter方法

使用JComponent.setBorder()设置组件的边框时,JComponent类触发属性改变事件,如果新的边框与旧边框不同,组件重新绘制。getBorder()方法简单返回Border引用。

图1为边框和JComponent类之间关系的类图。

澳门新葡亰平台游戏网站 1

图1 Swing边框

JComponent类包含Border对象的私有引用。注意由于Border是接口不是类,Swing组件可以拥有任意类型的实现了Border接口的边框(这就是对接口编程而不是对实现编程的含义)。

我们已经知道了JComponent是如何通过策略模式实现边框绘制的,下面创建一种新边框类型来测试一下它的可扩展性。

     2.
观察者模式

  1. 找到应用中可能需要变化之处,把它们独立出来,不要和那些需要变化的代码混在一起所有的设计模式都提供一套方法让系统中的某部分改变不会影响其他部分

  2. 针对接口编程,而不是针对实现编程利用接口代表每个行为,行为的实现不会影响具体的使用该行为的对象。客户可以利用接口引用每个具体对象,减少了客户和具体类之间的依赖。

  3. 多用组合,少用集成has-a比is-a具有更大的弹性和松耦合。JAVA中类的继承会限制复用潜力。

  4. 尽力实现交互对象之间的松耦合。

  5. 类应该对扩展开放,对修改关闭。在不修改基础不变类的代码的情况下进行功能的扩展。

  6. 要依赖抽象,不要依赖具体类不能让高层组件依赖底层组件。而且高层或底层组件,都应该依赖于抽象。倒置指的是原本高层依赖于低层,现在高低层都依赖中间抽象层。在设计中尽量避免依赖。

  7. 好莱坞原则别调用我们,我们会调用你。高层组件控制何时以及如何让底层组件参与,底层组件绝对不可以直接调用高层组件,底层组件可以参与计算。高层和底层有点类似与模版模式里的父类和子类,目的是不让高层组建依赖低层组件,但是低层组件能够被倒钩进计算里。

  8. 单一原则一个类应该只有一个引起变化的原因。

  9. 保持简单(Keep It
    Simple)模式常常会产生一些额外的类和对象,加入更多层,所以可能会引入复杂性并降低效率,你的目标应该是简单,而不是“如何在这个问题中应用模式”。学习管理软件的复杂度和变化,是程序员一生的课题。模式是工具而不是规则,需要被适当地调用以符合你的需求。

  10. 最少知识原则只和你的密友谈话。减少对象之间的交互。避免接触一个对象时需要注意它所交互的类有哪些,是如何和这些类交互的。缺点是会制造更多的包装类去处理和其它组件的沟通。以下为示例:

创建新的边框类型

澳门新葡亰平台游戏网站 2

图2 新边框类型

澳门新葡亰平台游戏网站 ,图2显示了具有三个面板的Swing应用。每个面板设置自定义的边框,每个边框对应一个HandleBorder实例。绘图程序通常使用handleBorder对象来移动对象和改变对象大小。

示例4为HandleBorder类:

import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
public class HandleBorder extends AbstractBorder {
   protected Color lineColor;
   protected int thick;
   public HandleBorder() {
      this(Color.black, 6);
   }
   public HandleBorder(Color lineColor, int thick) {
      this.lineColor = lineColor;
      this.thick = thick;
   }
   public void paintBorder(Component component, 
                                  Graphics g, int x, int y, int w, int h) {
      Graphics copy = g.create();
      if(copy != null) {
         try {
            copy.translate(x,y);
            paintRectangle(component,copy,w,h);
            paintHandles(component,copy,w,h);
         }
         finally {
            copy.dispose();
         }
      }
   }
   public Insets getBorderInsets() {
      return new Insets(thick,thick,thick,thick);
   }
   protected void paintRectangle(Component c, Graphics g,
                           int w, int h) {
      g.setColor(lineColor);
      g.drawRect(thick/2,thick/2,w-thick-1,h-thick-1);
   }
   protected void paintHandles(Component c, Graphics g,
                           int w, int h) {
      g.setColor(lineColor);
      g.fillRect(0,0,thick,thick); // upper left
      g.fillRect(w-thick,0,thick,thick); // upper right
      g.fillRect(0,h-thick,thick,thick); // lower left
      g.fillRect(w-thick,h-thick,thick,thick); // lower right
      g.fillRect(w/2-thick/2,0,thick,thick); // mid top
      g.fillRect(0,h/2-thick/2,thick,thick); // mid left
      g.fillRect(w/2-thick/2,h-thick,thick,thick); // mid bottom
      g.fillRect(w-thick,h/2-thick/2,thick,thick); // mid right
   }   
}

示例4 HandleBorder类

HandleBorder类继承自javax.swing.border.AbstractBorder,覆盖paintBorder()getBorderInsets()方法。尽管HandleBorder的实现不太重要,但是我们可以容易地创建新边框类型,因为Swing使用了策略模式绘制组件边框。

示例5为Swing应用。

import javax.swing.*;
import javax.swing.border.*;
import java.awt.*;
import java.awt.event.*;
public class Test extends JFrame {
   public static void main(String[] args) {
      JFrame frame = new Test();
      frame.setBounds(100, 100, 500, 200);
      frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
      frame.show();
   }
   public Test() {
      super("Creating a New Border Type");
      Container contentPane = getContentPane();
      JPanel[] panels = { new JPanel(), 
                     new JPanel(), new JPanel() };
      Border[] borders = { new HandleBorder(),
                     new HandleBorder(Color.red, 8),
                     new HandleBorder(Color.blue, 10) };
      contentPane.setLayout(
               new FlowLayout(FlowLayout.CENTER,20,20));
      for(int i=0; i < panels.length; ++i) {
         panels[i].setPreferredSize(new Dimension(100,100));
         panels[i].setBorder(borders[i]);
         contentPane.add(panels[i]);
      }
   }
}

示例5 使用handleBorder

前面的应用创建了三个面板(javax.swing.JPanel实例)和三个边框(HandleBorder实例)。注意通过调用JComponent.setBorder()可以为面板简单设置具体的边框。

回想一下示例2,当JComponent调用Border.paintBorder()时,组件引用传递给组件的边框——一种委托方式。正如我前面提到的,开发人员经常将策略模式与委托共同使用。该HandleBorder类未使用组件引用,但是其他边框会用到引用从组件获取信息。比如示例6为这种类型边框javax.swing.border.EtchedBorderpaintBorder()方法:

// The following listing is from
// javax.swing.border.EtchedBorder
public void paintBorder(Component component, Graphics g, int x, int y, 
                         int width, int height) {
   int w = width;
   int h = height;

   g.translate(x, y);

   g.setColor(etchType == LOWERED? getShadowColor(component) : getHighlightColor(component));
   g.drawRect(0, 0, w-2, h-2);

   g.setColor(etchType == LOWERED? getHighlightColor(component) : getShadowColor(component));
   g.drawLine(1, h-3, 1, 1);
   g.drawLine(1, 1, w-3, 1);

   g.drawLine(0, h-1, w-1, h-1);
   g.drawLine(w-1, h-1, w-1, 0);

   g.translate(-x, -y);
}

示例6 从组件获取信息的Swing边框

javax.swing.border.EtchedBorder.paintBorder()方法使用它的组件引用获取组件的阴影和高亮颜色信息。

         
在对象之间定义一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象都会收到通知,并自动更新

实现策略模式

策略模式相对比较简单,在软件中容易实现:

  1. 为你的策略对象定义Strategy接口
  2. 编写ConcreteStrategy类实现Strategy接口
  3. 在你的Context类中,保持对“`Strategy“对象的私有引用。
  4. 在你的Context类中,实现Strategy对象的settter和getter方法。

Strategy接口定义了Strategy对象的行为;比如Swing边框的Strategy接口为javax.swing.Border接口。

具体的ConcreteStrategy类实现了Strategy接口;比如,Swing边框的LineBorderEtchedBorder类为ConcreteStrategy类。Context类使用Strategy对象;比如JComponent类为Context对象。

你也可以检查一下你现有的类,看看它们是否是紧耦合的,这时可以考虑使用策略对象。通常情况下,这些包括switch语句的需要改进的地方与我在文章开头讨论的非常相似。

 

/***不推荐。从气象站取得温度计(Thermometer)后,再通过温度计对象取得温度。*/public float getTemperature() { Thermometer thermometer = station.getThermometer(); return thermomeer.getTemperature();}/***推荐使用。将获取温度的操作封装到气象站中做,客户不需要接触过多数量的对象。*/pulic float getTemperature() { return station.getTemperature();}

作业

一些Swing组件的渲染和编辑条件比其他的更加复杂。讨论如何在列表类(javax.swing.JList)使用策略模式渲染列表项。

     3.
装饰者模式

  1. 策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变换独立于使用算法的客户。适用对象组合的方式让客户可以选择算法实现。可理解成是除了继承之外的一种弹性替代方案,可组合不同对象来改变行为。

  2. 观察者模式定义为对象之间的一对多依赖,当一个对象改变状态时,他的所有依赖者都会收到通知并自动更新。Android为很多GUI(Graphical
    User
    Interface)控件(Button/ViewPager等)添加Listener监听,内部使用了观察者模式。

  3. 装饰者模式动态的将责任附加到对象上。想要扩展功能,装饰者提供了比继承更有弹性的替代方案。通常装饰者和被装饰者具有相同的超类型,装饰者在被装饰者的行为之前/之后加上自行的行为进行修饰。I/O的API中用到了装饰者模式:LineNumberInputSrream(BufferedInputStream(FileInputStream(InputSream))));

  4. 工厂模式定义了一个创捷对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。简单工厂模式:提供一个工厂负责制造多类型的产品,工厂是全能类工厂模式:多个工厂各自负责单一类型产品的构建,工厂为单一职责

  5. 抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
    如果工厂的产品全部属于同一个等级结构则属于工厂模式,如果来自多个等级结构则属于抽象工厂模式。

  6. 单例模式确保一个类只有一个实例,并提供一个全局访问点。可以在需要的时候才创建对象。Java中实现单例模式需要私有的构造器、一个静态方法和一个静态变量。没有公开的构造器,只能通过getInstance()获得单一对象。JVM在加载类时会立即创建唯一的单例事例。

  7. 命令模式将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。[应用]
    日程安排,日志及事务请求,线程池,工作队列

  8. 适配器模式将一个类的接口转换成客户期望的另一个接口。适配器让原来接口不兼容的类可以合作无间。

  9. 外观模式提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。这个模式应用非常之广,比如Retrofit.create()方法就是使用了外观模式。

上一次的作业

上一次的作业要求重新实现TableBubbleSortDecorator。在“装饰你的代码”一文首先讨论了JDK内建的对代理模式的支持。

简单来说,我创建了抽象类Decorator实现java.lang.reflect.InvocationHandler接口。Decorator类引用了装饰对象(或者说代理模式中的真实对象)。示例1H为Decorator类。

import java.lang.reflect.InvocationHandler;
public abstract class Decorator implements InvocationHandler {
   // The InvocationHandler interface defines one method:
   // invoke(Object proxy, Method method, Object[] args). That
   // method must be implemented by concrete (meaning not 
   // abstract) extensions of this class.
   private Object decorated;
   protected Decorator(Object decorated) {
      this.decorated = decorated;
   }
   protected synchronized Object getDecorated() {
      return decorated;
   }
   protected synchronized void setDecorated(Object decorated) {
      this.decorated = decorated;
   }
}

示例1H 抽象装饰器类

尽管Decorator类实现了InvocationHandler接口,但是它没有实现该接口的唯一方法invoke(Object proxy, Method method, Object[] methodArguments)。因为Decorator类是抽象的,Decorator的扩展是具体类的话必须实现invoke()方法。

Decorator类是所有装饰器的基类。示例2H为Decorator类的扩展,具体的表排序装饰器。注意TableSortDecorator没有实现invoke()方法,它是抽象的。

import javax.swing.table.TableModel;
import javax.swing.event.TableModelListener;
public abstract class TableSortDecorator 
                                 extends Decorator 
                                 implements TableModelListener {
   // Concrete extensions of this class must implement 
   // tableChanged from TableModelListener
   abstract public void sort(int column);
   public TableSortDecorator(TableModel realModel) {
      super(realModel);
   }
}

示例2H 修正的TableSortDecorator

现在可以使用JDK内建的对代理模式的支持实现TableBubbleSortDecorator

import java.lang.reflect.Method;
import javax.swing.table.TableModel;
import javax.swing.event.TableModelEvent;
public class TableBubbleSortDecorator extends TableSortDecorator {
   private int indexes[];
   private static String GET_VALUE_AT = "getValueAt";
   private static String SET_VALUE_AT = "setValueAt";
   public TableBubbleSortDecorator(TableModel model) {
      super(model);
      allocate();
   }
   // tableChanged is defined in TableModelListener, which
   // is implemented by TableSortDecorator.
   public void tableChanged(TableModelEvent e) {
      allocate();   
   }
   // invoke() is defined by the java.lang.reflect.InvocationHandler
   // interface; that interface is implemented by the 
   // (abstract) Decorator class. Decorator is the superclass
   // of TableSortDecorator.
   public Object invoke(Object proxy, Method method, 
                                         Object[] args) {
      Object result = null;
      TableModel model = (TableModel)getDecorated();
      if(GET_VALUE_AT.equals(method.getName())) {
         Integer row = (Integer)args[0], 
                 col = (Integer)args[1];
         result = model.getValueAt(indexes[row.intValue()], 
                                           col.intValue());
      }
      else if(SET_VALUE_AT.equals(method.getName())) {
         Integer row = (Integer)args[1],
                 col = (Integer)args[2];
         model.setValueAt(args[0], indexes[row.intValue()],
                                   col.intValue());
      }
      else {
         try {
            result = method.invoke(model, args);
         }
         catch(Exception ex) {
            ex.printStackTrace(System.err);
         }
      }
      return result;
   }
   // The following methods perform the bubble sort ...
   public void sort(int column) {
      TableModel model = (TableModel)getDecorated();
      int rowCount = model.getRowCount();
      for(int i=0; i < rowCount; i++) {
         for(int j = i+1; j < rowCount; j++) {
            if(compare(indexes[i], indexes[j], column) < 0) {
               swap(i,j);
            }
         }
      }
   }
   private void swap(int i, int j) {
      int tmp = indexes[i];
      indexes[i] = indexes[j];
      indexes[j] = tmp;
   }
   private int compare(int i, int j, int column) {
      TableModel realModel = (TableModel)getDecorated();
      Object io = realModel.getValueAt(i,column);
      Object jo = realModel.getValueAt(j,column);
      int c = jo.toString().compareTo(io.toString());
      return (c < 0) ? -1 : ((c > 0) ? 1 : 0);
   }
   private void allocate() {
      indexes = new int[((TableModel)getDecorated()).
                          getRowCount()];
      for(int i=0; i < indexes.length; ++i) {
         indexes[i] = i;         
      }
   }
}

示例3H 修正的TableBubbleSortDecorator

使用JDK内建的对代理模式的支持和设计良好的基类,通过继承Decorator及实现invoke()方法很容易实现装饰器。

         
动态的将责任附加到对象上。想哟啊扩展功能,装饰者提供有别于继承的另一种选择

外观模式/适配器模式/装饰者模式
三者区别:适配器:将一个对象包装起来以改变其接口装饰者:将一个对象包装起来以增加新的行为和责任外观:将一对象“包装”起来以简化接口

邮件

给我的一封邮件里这样写到:

根据我在树上选择的节点工具栏要显示特定的按钮。我创建了工具栏装饰器,它的构造函数参数为JToolBar工具栏。装饰器包含一个showButtonForNode()方法根据节点改变按钮。我调用在树的选择监听器的valueChanged()方法中调用showButtonForNode()方法。
这样使用装饰器模式正确吗?

很多设计模式可以达到功能扩展的目的;比如在Java设计模式中,你已经知道如何使用代理模式,装饰器模式和策略模式来扩展功能。由于他们都可以实现相同的目标(功能扩展),在具体情况下使用哪个模式就很难判断。

装饰器模式的主要解决问题的点在于:在运行时结合多种行为;比如理解代理设计模式一文的“上一次得作业”部分,我展示了Swing表格排序和过滤相结合的方法。

TableSortDecorator sortDecorator = new TableBubbleSortDecorator(table.getModel());
TableFilterDecorator filterDecorator = new TableHighPriceFilter(sortDecorator);
table.setModel(filterDecorator);

前面的代码中,过滤装饰器装饰了排序装饰器,排序装饰器装饰了表格模型;结果表格模型可以排序和过滤数据。

对于邮件中的问题,使用工具栏按钮与其他行为组合不太合适,所以装饰器模式可能不合适。这种情况代理模式看来更好,在编译阶段而不是运行时就可以获取代理和真实对象的关系,从而扩展功能。

     
要点:

  1. 模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模版方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。模版方法定义了一个算法的步骤,并允许子类为一个或多个步骤提供实现。比如Array的排序需要子类实现comparable接口的compareTo()方法.继承Activity的页面都是用了模版方法。模版方法的抽象类可定义具体方法,抽象方法和钩子(子类可选择是否覆盖该默认实现)。

  2. 迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。把元素之间遍历游走的顺序交给迭代器而不是集合对象,让集合更专注在它所应该专注的事情上面。

  3. 组合模式将对象组合成树形结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象以及对象组合。组合包含组件,组件有两种:组合与叶节点元素。类似View及ViewGroup的关系。

  4. 状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。将状态封装成为独立的类,将动作委托到代表当前状态的对象。如果使用的对象能够完全改变它的行为,就好像这个对象是从别的类实例化而来的,其实不是。Context会将行为委托给当起对象。一般来讲,当状态是固定的时候,就适合将状态转换的流向放在context控制类中;然而,当状态是更动态的时候,通常就会把状态转换的流向放在状态类中。扩展:UML类图就比如播放器MediaPlayer的各种状态都是一个具有行为的对象。

  5. 代理模式为另一个对象提供一个替身或占位符以控制对这个对象的访问。使用代理模式创建代表对象,让代表对象控制某对象的访问,被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象。装饰者为对象增加行为。代理是控制对象的访问。

  • 继承属于扩展形式之一,但不见得是达到弹性设计的最佳方式
  • 在我们的设计中,应该允许行为可以被扩展,而无需修改现有的代码
  • 组合和委托可用于在运行时动态地加上新的行为
  • 除了继承,装饰者模式也可以让我们扩展行为
  • 装饰者模式意味着一群装饰者类,这些类用来包装具体组件
  • 装饰者类反应出被装饰者的组件类型(事实上,他们具有相同的类型,都是经过接口或继承实现)
        
  • 装饰者可以被装饰者的行为前面与/或后面加上自己的行为,甚至将被装饰者的行为整个取代掉,而达到特定的目的
  • 可以用无数个装饰者包装一个组件
  • 装饰者一般对组件的客户是透明的,除非客户程序依赖于组件的具体类型
  • 装饰者会导致设计中出现许多小对象,如果过度使用,会让程序变得复杂

远程代理:管理客户和远程对象之间的交互。远程代理可以作为另一个JVM上对象的本地代表。代用代理的方法,会被代理利用网络转发到远程执行,并且结果会通过网络返回给代理。虚拟代理:控制访问实例化开销大的对象。虚拟代理作为创建开销大的对象的代表。虚拟代理经常知道我们真正需要一个对象的时候才创建它。当对象再创建前和创建中时,由虚拟代理来扮演对象的替身。对象创建后,代理就会将请求直接委托给对象。保护代理:也称动态代理。基于调用者控制对象方法的访问。可根据访问权限决定客户可否访问对象的代理。

 

  1. 复合模式复合模式在一个解决方案中结合两个或多个模式,以解决一般或重复发生的问题。复合模式必须具有一般性,适合解决许多问题才行。

   
 4.

MVCModel-View-Controller就是复合模式,Android中的典型应用是LiewView:(Model-ListView-Adapter);M层处理数据,业务逻辑等;V层处理界面的显示结果;C层起到桥梁的作用,聪明的将来自试图的动作转换成模型上的动作。控制器把控制逻辑从视图中分离,减少视图的责任,让模型和视图之间解耦。模式:视图和控制器实现了策略模式。视图可以被调整使用不同的控制器提供的策略,任何界面行为都委托给控制器处理,控制器负责和模型交互来传递用户的请求。视图和模型实现了观察者模式
。模型是被观察者,视图和控制器是观察者。视图用了组合模式实现GUI。

     
抽象工厂模式:

  1. 桥接模式将抽象部分与它的实现部分分离,使它们都可以独立地变化。不止改变你的实现,也改变你的抽象。适合使用在需要跨越多个平台的图形和窗口系统上。当需要用不同的方式改变接口和实现时,桥接模式很好用。

  2. 生成器模式封装一个产品的构造过程,并需要按步骤构造。将一个复杂对象的创建过程封装起来,允许对象通过多个步骤来创建,并且可以改变过程。经常被用来创建组合结构。

  3. 责任链模式当你想要让一个以上的对象有计划能够处理某个请求的时候,就使用责任链模式。为某个请求创建一个对象链,每个对象一次检查此请求,并对其进行处理,或者将它传给链中的下一个对象。将请求的发送者和接收者解耦。(okHttp,
    Fresco, RxJava都有使用)

  4. 蝇量模式如果想让某个类的一个实例能用来提供许多“虚拟实例”,就使用蝇量模式。的那个一个类又许多实例,而这些实例能被同一方法控制的时候,可使用蝇量。可将需要“虚拟”对象的状态集中管理。减少运行时对象实例的个数,节省内存。

  5. 解释器模式为语言创建解释器。将每一个语法规则表示成一个类,方便于实现语言。可以处理脚本语言和编程语言。要想解释这种语言,就调用每个表达式类型的interpret()方法,此方法需要传入一个上下文Context——也就是我们正在解析的语言字符串输入流——然后进行对比并采取适当的动作。

  6. 中介者模式使用中介者模式来集中相关对象之间复杂的沟通和控制方式。每个对象都会在自己的状态改变时,告诉中介者。每个对象都会对中介者所发出的请求作出回应。中介者内包含了整个系统的控制逻辑。通过将对象之间彼此解耦,可以增加对象的复用性。通过将控制逻辑集中,可以简化系统维护。中介者常常被用来协调相关的GUI组件。

  7. 备忘录模式存储系统关键对象的重要状态,维护关键对象的封装。用户存储状态。将被储存的状态放在外面,不要和关键对象混在一起,这可以帮助维护内聚。在Java系统中,可以考虑使用序列化(serialization)机制存储系统的状态。Activity及Fragment的saveInstanceState有用到。

  8. 原型模式当创建给定的类的实例的过程很昂贵或很复杂时,就使用原型模式。允许你通过复制现有的实例来创建新的实例(在Java中,这通常意味着使用clone()方法或者反序列方法)。客户的代码在不知道要实例化何种特定类的情况下,可以制造新的实例。在一个复杂的类层次中,但系统必须从其中的许多类型创建新对象时,可以考虑原型。

  9. 访问者模式当你想要为一个对象的组合增加新的能力,且封装并不重要时,就使用访问者模式。访问者必须参观组合内的每个元素,在导游traverser对象中,访问者通过导游的引导,收集组合中所有对象的状态。一旦状态被收集了,客户就可以让访问者对象进行各种操作。允许你对组合结构加入新的操作,而无需改变结构本身。

         
提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类

抽象,封装,多态,继承

   
 工厂方法模式:

多态

多态分为编译时多态和运行时多态。其中编辑时多态是静态的,主要是指方法的重载,它是根据参数列表的不同来区分不同的函数,通过编辑之后会变成两个不同的函数,在运行时谈不上多态。而运行时多态是动态的,它是通过动态绑定来实现的,也就是我们所说的多态性。Java实现运动时多态有三个必要条件:继承、重写、向上转型。牢牢记住每个设计模式是没有必要的,但是这是必经的一步。重要的是,要记住设计模式的思想。先看定义,再看思想。

虽然大多数设计模式都会增加类,但是真正重要的是你暴露给使用者的类数目,设计模式能够将额外增加的类隐藏起来。但是这些新增的类有利于业务增长。在设计的过程中,折衷一直都是免不了的。

  1. 在实际开发中会发现其中的设计模式。在写了一点代码之后学习设计模式会对这些模式有明确的实例关联,让人有意识的去总结并在之后应用这种模式。比如之前一直觉得哇这个功能类之间的架构好牛逼啊,只知道具体应用了继承重写重载等技巧和具体流程的逻辑,但是真的没有去进一步总结这种模式。
  2. 帮助开发过程中运用设计模式。这次看设计模式会有一种总览的感觉,应该会让之后写代码更有设计观一点。这对于开发者理解和运用设计模式有很大帮助。而且这种学习没有阶层限制,但是一定要相信,你在学会一些入门知识以及业务工作后,会到达另一种状态。每个类都不知道其他类的细节,但是多个类实现了一个具体的功能需求,神奇。

         
定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类

   
 要点:

  • 所有的工厂都是用来封装对象的创建
  • 简单工厂,虽然不是真正的设计模式,但是仍不失为一个简单的方法,可以将客户程序从具体类解耦
  • 工厂方法使用继承:把对象的创建委托给子类,子类实现工厂方法来创建对象
  • 抽象工厂使用对象组合:对象的创建被实例化在工厂接口所暴露出来的方法中
  • 所有工厂模式都通过减少用功程序的具体类之间的依赖促进松紧耦合
  • 工厂方法允许类将实例化延迟到子类进行
  • 抽象工厂创建相关的对象家族,而不需要依赖他们的具体类
  • 依赖倒置原则,指导我们避免依赖具体类型,而尽量依赖抽象
  • 工厂是很有威力的技巧,帮助我们针对抽象编程,而不要针对具体类型编程

   
 5.单件(例)模式

         
确保一个类只有一个实例,并提供全局访问点

   
 要点:

  • 单件模式确保程序中一个类最多只有一个实例
  • 单件模式也提供访问这个实例的全局点
  • 在Java中实现单件模式需要私有化构造器,一个静态方法和一个静态变量
  • 确定在性能和资源上的限制,然后小心地选择适当的方案来实现的那件,以解决多线程的问题
  • 如果使用多个类加载器。可以导致单件失效而产生多个实例
  • 如果使用JVM1.2或之前的版本,必须建立注册表,以免垃圾收集器将的单件回收

   
 6.命令磨时间哦

         
将请求封装成对象,这可以让你使用不同的情iqu,队列,或者日志请求来参数化其他对象。命令模式也可以支持撤销操作

   
 要点:

  • 在被解耦的两者之间是通过命令对象昂进行沟通的,命令对象封装了接受者和一个或一组动作
  • 调用者通过调用命令对象的execute()发出请求,这会使得接受者的动作被调用
  • 调用者可以接受命令当作参数,甚至在运行时动地进行
  • 命令可以支持撤销,做法是实现一个undo()方法来回到execute()被执前的状态
  • 宏命令是命令的一种简单延伸,允许调用多个命令。宏方法也可以支持撤销
  • 实际操作时,很常见使用“聪明”对象,也就是直接实现了请求,而不是将工作委托给接受者
  • 命令也可以用来实现日志和事物系统

   
 

   
 7.

   
 适配器模式:

         
将一个类让接口,转换成客户期望的另一个接口。适配器让原本不兼容的类可以合作无间

   
 外观模式:

         
提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易调用

   
 要点:

  • 当需要使用一个现有的类而其接口并不符合你的需要时,就是用适配器
  • 当需要简化并同意一个很大的接口或者一群复杂的接口时,使用外观
  • 适配器改变接口以符合客户的期望
  • 外观将客户从一个复杂的子系统中解耦出来
  • 实现一个适配器可能需要一番功夫,也可能不费功夫,视目标接口的大小与复杂度而定
  • 实现一个外观,需要将子系统组合外观中,然后将工作委托给子系统执行
  • 适配器模式有两种形式:对象适配器和类适配器。类适配器需要用到多重继承(Java不支持多重继承)
  • 适配器将一个对象包装起来一改变接口;装饰者将一个对象包装起来以增加新的行为和责任;外观将一群对象“包装”起来以简化其接口

 

   
 8.模板方法模式

         
在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法是的子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤

   
 要点:

  • “模板方法”定义了算法的步骤,把这些步骤的实现延迟到子类
  • 模板方法为我们提供了一种代码复用的重要技巧
  • 模板方法的抽象类可以定义具体方法,抽象方法和钩子
  • 抽象方法由子类实现
  • 钩子是一种方法,它在抽象类中不做事,或者只做默认的事情,子类可以选择要不要去覆盖它
  • 为了防止子类改变模板方法中的算法,可以将模板方法声明为final
  • 好莱坞原则告诉我们,将决策权放在高层模板中,以便决定如何以及何时调用低层模板
  • 你将在真是世界代码中看到模板方法模式的多变体,不要期待他们全都是一眼就可以被你认出来的
  • 策略模式和模板方法模式都封装算法,一个用组合,一个用继承
  • 工厂方法是模板方法的一种特殊版本

   
 

   
 9. 

   
 迭代器模式:

         
提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示

   
 组合模式:

         
允许你将对象组成树形结构来表示“整体/部分”的层次结构。组合能让客户以一致的方式处理个别对象和对象组合

   
 要点:

  • 迭代器允许访问聚合的元素,而不需要暴露他的内部结构
  • 迭代器将遍历聚合的工作封装进一个对象中
  • 当使用迭代器的时候,我们依赖聚合提供遍历
  • 迭代器提供了一个通用的接口,让我们遍历聚合的项,当我们编码使用聚合的项时,就可以使用多态机制
  • 我们应该努力让一个结构,可同时包容个别对象和组合对象
  • 组合模式允许客户对个别对象及组合对象一视同仁
  • 组合结构内的任意对象称为组件,组件可以是组合,也可以是叶节点
  • 在实现组合模式时,有许多设计上的折中。你要根据组要平衡透明性和安全性

 

     10.
状态模式

         
允许对象在内部状态改变是改变他的行为,对象看起来好像修改的他的类

   
 要点:

  • 状态模式允许一个对象基于内部状态而拥有不同的行为
  • 和程序状态机不同,状态模式用类代表状态
  • context会将行为封装进一个类,我们把以后需要做的任何改变局部化了
  • 状态模式和策略模式有相同的类图,但是他们的意图不同
  • 策略模式通常或用行为域算法类配置context类
  • 状态模式允许context随着状态的改变而改变行为
  • 状态转换可以由state类或context类控制
  • 使用状态
    模式通常会导致设计中类的数目大量增加
  • 状态类可以被多个context实例共享

 

     11.
代理模式

         
为另一个对象提供一个替身或占位符以访问这个对象

   
 要点:

  • 代理模式为另一个对象提供代表,以便控制客户对对象的访问,管理访问的方式有许多种
  • 远程代理客户和远程对象之间的交互
  • 虚拟代理控制访问实例化开销大的对象
  • 保护代理基于调用者对对象方法的访问
  • 代理模式有许多变体,例如:缓存代理,同步代理,防火墙代理和写入时复制代理
  • 代理在结构上类似于装饰者,但是目的不同
  • 装饰者模式为对象加上行为,而代理则是控制访问
  • Java内置的代理支持,可以根据需要建立动态代理,并将所有调用分配到所选的处理器
  • 就和其他的包装者一样,代理会造成设计中类的数量增加

 

     12.
复合模式

         
   
 复合模式结合两个或以上的模式,组成一个解决方案,解决一再发生的一般性问题。(MVC,model2)

   
 要点:

  • MVC是复合模式,结合观察者模式,策略模式和组合模式
  • 模型使用观察者模式,以便观察者更新,同时保持两者之间的解耦
  • 控制器是视图的策咯,视图可以使用不同的控制器实现,得到不同的行为
  • 视图使用组合模式实现用户界面,用户界面通常组合了嵌套的组件,项面板,框架和按钮
  • 这些模式携手合作,把MVC模型的三层解耦,这样可以保持设计干净又有弹性
  • 适配器模式用来将新的模型适配成已有的视图和控制器
  • Model2是MVC在web上的应用
  • 在Model2中,控制器实现成Servlet,而JSP/HTML实现视图

 

 

 

 

 

 

 

 

 

 

 

 

 

You can leave a response, or trackback from your own site.

Leave a Reply

网站地图xml地图