上一篇我们介绍了设计模式的特性并且详细讲解了4种创建型模式,创建型模式是负责如何产生对象实例的,现在我们继续来给大家介绍结构型模式。

一、什么是结构型模式?    结构型模式是解析类和对象的内部结构和外部组合,通过优化程序结构解决模块之间的耦合问题。
二、结构型模式的种类:     适配器模式     桥接模式     装饰模式     组合模式     外观模式     享元模式    代理模式
1、 适配器模式(Adapter)



练练手

/**              


                    * 优才网公开课示例代码              


                    *              


                    * 适配器模式              


                    *              


                    * @author 优才网全栈工程师教研组              


                    * @see http://www.ucai.cn             


                    */         


                    


          //老的代码               


          class          User {              


                    private          $name          ;              


                    function          __construct(          $name          ) {              


                    $this          ->name =           $name          ;              


                    }              


                    public          function          getName() {              


                    return          $this          ->name;              


                    }              


          }              


                    


          //新代码,开放平台标准接口              


          interface          UserInterface {              


                    function          getUserName();              


          }              


          class          UserInfo           implements          UserInterface {              


                    protected          $user          ;              


                    function          __construct(          $user          ) {              


                    $this          ->user =           $user          ;              


                    }              


                    public          function          getUserName() {              


                    return          $this          ->user->getName();              


                    }              


          }              


                    


          $olduser          =           new          User(          '张三'          );              


          echo          $olduser          ->getName().          "n"          ;              


          $newuser          =           new          UserInfo(          $olduser          );              


          echo          $newuser          ->getUserName().          "n"          ;     



 

 

     注意点:这里的新接口使用了组合方式,UserInfo内部有一个成员变量保存老接口User对象,模块之间是松耦合的,这种结构其实就是组合模式。不要使用继承,虽然UserInfo继承User也能达到同样的目的,但是耦合度高,相互产生影响。
    2、 桥接模式

    应用:需要不断更换调用对象却执行相同的调用方法,实现扩展功能

   代码实现    



练练手

/**              


                    * 优才网公开课示例代码              


                    *              


                    * 桥接模式              


                    *              


                    * @author 优才网全栈工程师教研组              


                    * @see http://www.ucai.cn             


                    */         


                    


          abstract          class          Person {              


                    abstract          function          getJob();              


          }              


                    


          class          Student           extends          Person {              


                    public          function          getJob() {              


                    return          '学生'          ;              


                    }              


          }              


                    


          class          Teacher           extends          Person {              


                    public          function          getJob() {              


                    return          '老师'          ;              


                    }              


          }              


                    


          class          BridgeObj {              


                    protected          $_person          ;              


                    


                    public          function          setPerson(          $person          ) {              


                    $this          ->_person =           $person          ;              


                    }              


                    


                    public          function          getJob() {              


                    return          $this          ->_person->getJob();              


                    }              


          }              


                    


          $obj          =           new          BridgeObj();              


          $obj          ->setPerson(          new          Student());              


          printf(          "本次桥接对象:%sn"          ,           $obj          ->getJob());              


          $obj          ->setPerson(          new          Teacher());              


          printf(          "本次桥接对象:%sn"          ,           $obj          ->getJob());




3、 装饰模式       动态地给一个对象添加额外的职责。在原有的基础上进行功能增强。       特点:用来增强原有对象功能,依附于原有对象。       应用:用于需要对原有对象增加功能而不是完全覆盖的时候
代码实现



练练手

/**               


                    * 优才网公开课示例代码               


                    *               


                    * 装饰模式               


                    *               


                    * @author 优才网全栈工程师教研组               


                    * @see http://www.ucai.cn              


                    */         


                    


          //产品               


          abstract          class          Person {               


                    abstract          function          getPermission();               


          }               


          //被装饰者               


          class          User           extends          Person {               


                    public          function          getPermission() {               


                    return          '公开文档'          ;               


                    }               


          }               


          //装饰类               


          class          PermUser           extends          Person {               


                    protected          $_user          ;               


                    protected          $_special          =           ''          ;               


                    function          __construct(          $user          ) {               


                    $this          ->_user =           $user          ;               


                    }               


                    public          function          getPermission() {               


                    return          $this          ->_user->getPermission() .           $this          ->_special;               


                    }               


          }               


          //装饰类产品               


          class          JavaUser           extends          PermUser {               


                    protected          $_special          =           ' java程序'          ;               


          }               


          class          CPlusUser           extends          PermUser {               


                    protected          $_special          =           ' c++程序'          ;               


          }               


                    


                    


          $user          =           new          User();               


          printf(          "permission:%sn"          ,           $user          ->getPermission());               


          $user          =           new          JavaUser(          $user          );               


          printf(          "permission:%sn"          ,           $user          ->getPermission());               


          $user          =           new          CPlusUser(          $user          );               


          printf(          "permission:%sn"          ,           $user          ->getPermission());      




      大家想想装饰和继承的区别在哪?       如果是上面的例子,如果用继承,是CPlusUser继承JavaUser还是反过来呢?谁也不知道最终使用者需要哪一种。

     在多层关系的情况下,装饰是和顺序无关并且随时增加装饰,而继承只能是特定的顺序,所以装饰模式会更加的灵活。

4、组合模式



练练手

/**               


                    * 优才网公开课示例代码               


                    *               


                    * 组合模式               


                    *               


                    * @author 优才网全栈工程师教研组               


                    * @see http://www.ucai.cn              


                    */         


                    


          //继承模式               


                    


          class          UserBaseInfo {               


                    private          $name          ;               


                    


                    function          __construct(          $name          ) {               


                    $this          ->name =           $name          ;               


                    }               


                    public          function          getName() {               


                    return          $this          ->name;               


                    }               


          }               


          class          User           extends          UserBaseInfo {               


                    private          $login          = false;               


                    


                    public          function          setLogin(          $islogin          ) {               


                    $this          ->login =           $islogin          ;               


                    }               


                    public          function          isLogin() {               


                    return          $this          ->login;               


                    }               


          }               


                    


          $user          =           new          User(          '张三'          );               


          $user          ->setLogin(true);               


          if          (          $user          ->isLogin()) {               


                    echo          $user          ->getName().          "已经登录了n"          ;               


          }           else          {               


                    echo          $user          ->getName().          "还没有登录n"          ;               


          }               


                    


                    


          //组合模式               


                    


          class          LoginInfo {               


                    protected          $user          ;               


                    protected          $login          = false;               


                    


                    public          function          setLogin(          $user          ,           $isLogin          ) {               


                    $this          ->user =           $user          ;               


                    $this          ->login =           $isLogin          ;               


                    }               


                    public          function          isLogin() {               


                    return          $this          ->login;               


                    }               


          }               


                    


          $user          =           new          User(          '张三'          );               


          $login          =           new          LoginInfo();               


          $login          ->setLogin(          $user          , true);               


          if          (          $login          ->isLogin()) {               


                    echo          $user          ->getName().          "已经登录了n"          ;               


          }           else          {               


                    echo          $user          ->getName().          "还没有登录n"          ;               


          }               


                    


          //部分可以更换,用继承则不行               


          class          Admin {               


                    protected          $level          ;               


                    function          __construct(          $level          ) {               


                    $this          ->level =           $level          ;               


                    }               


                    function          getLevel() {               


                    return          $this          ->level;               


                    }               


          }               


          $admin          =           new          Admin(1);               


          $login          ->setLogin(          $admin          , true);               


          if          (          $login          ->isLogin()) {               


                    printf(          "级别为 %d 的管理员已经登录了n"          ,           $admin          ->getLevel());               


          }           else          {               


                    printf(          "级别为 %d 的管理员还没有登录n"          ,           $admin          ->getLevel());               


          }



 

        上面的例子分别展示了使用继承和组合来处理新功能,在简单的情况下看似区别不大,但在项目后期越来越复杂的时候组合模式的优势就越来越明显了。       例如上面的登录信息,如果要增加登录次数、最后登录时间、登录ip等信息,登录本身就会变成一个比较复杂的对象。如果以后有新的需求比如好友信息、用户的 访问信息等,再要继承的话,用户类就会变得非常庞大,难免各父类之间没有冲突的变量和方法,而外部访问用户类的众多方法也变得很费劲。采用组合模式后,一 个类负责一个角色,功能区分非常明显,扩展方便。
      5、 外观模式(门面模式)       为了系统中的一组接口提供一个一致的界面       特点:向上抽取,有共性       应用:内部接口众多,由统一的接口来调用
代码实现      



练练手

/**               


                    * 优才网公开课示例代码               


                    *               


                    * 外观模式,也叫门面模式               


                    *               


                    * @author 优才网全栈工程师教研组               


                    * @see http://www.ucai.cn              


                    */         


                    


                    


          class          Operation {               


                    public          function          testPlus() {               


                    printf(          "plus: %sn"          , (1 + 2 == 3 ?           'true'          :           'false'          ));               


                    }               


                    public          function          testMinus() {               


                    printf(          "minus: %sn"          , (3 - 2 == 2 ?           'true'          :           'false'          ));               


                    }               


                    public          function          testTimes() {               


                    printf(          "times: %sn"          , (2 * 3 == 6 ?           'true'          :           'false'          ));               


                    }               


          }               


                    


          class          Tester {               


                    protected          $_operation          ;               


                    function          __construct() {               


                    $this          ->_operation =           new          Operation();               


                    }               


                    public          function          testAll() {               


                    $this          ->_operation->testPlus();               


                    $this          ->_operation->testMinus();               


                    $this          ->_operation->testTimes();               


                    }               


          }               


                    


          //测试用例,测试全部接口               


          $tester          =           new          Tester();               


          $tester          ->testAll();      



       门面模式估计大家在实际代码中都已经使用到了,接口较多时把相似功能的接口封装成一个接口供外部调用,这就是门面模式。

6、 享元模式       运用共享技术有效地支持大量细粒度对象,采用一个共享来避免大量有相同内容对象的开销。这种开销中最直观的就是内存的损耗。       特点:高效性,共享性       应用:系统底层的设计。例如字符串的创建。如果两个字符串相同,则不会创建第二个字符串,而是第二个的引用直接指向第一个字符 串。$str1=”abc”,$str2=”abc”.则内存存储中只会创建一个字符串“abc”而引用$str1.$str2都会指向它。
7、  代理模式        为其他对象提供一个代理来控制对这个对象的访问,就是给某一对象提供代理对象,并由代理对象控制具体对象的引用。能够协调调用者和被调用者,能够在一定程度上降低系统的耦合性。       特点:低耦合性,独立性好,安全性       应用:客户访问不到或者被访问者希望隐藏自己,所以通过代理来访问自己。
代码实现      



练练手

/**               


                    * 优才网公开课示例代码               


                    *               


                    * 代理模式               


                    *               


                    * @author 优才网全栈工程师教研组               


                    * @see http://www.ucai.cn              


                    */         


                    


          //内部对象               


          class          User {               


                    public          function          getName() {               


                    return          '张三'          ;               


                    }               


                    public          function          getType          () {               


                    return          '付费用户'          ;               


                    }               


          }               


                    


          //代理接口定义,例如开放平台               


          interface          UserInterface {               


                    function          getName();               


          }               


          //代理对象               


          class          UserProxy           implements          UserInterface {               


                    protected          $_user          ;               


                    function          __construct() {               


                    $this          ->_user =           new          User();               


                    }               


                    public          function          getName() {               


                    return          $this          ->_user->getName();               


                    }               


          }               


                    


          //内部调用               


          $user          =           new          User();               


          printf(          "user name:%sn"          ,           $user          ->getName());               


          printf(          "user type:%sn"          ,           $user          ->          getType          ());               


          //外部调用               


          // $user = new UserProxy();               


          // printf("user name:%sn", $user->getName());               


          // printf("user type:%sn", $user->getType()); //不能访问,及时知道内部对象有这个方法



 

三、总结      1、代理模式、适配器模式、门面模式、装饰模式的区别           a、 相同之处:都封装一个内部对象,调用内部对象的方法           b、 不同之处:各自有各自的特性和应用场景,不能相互替代。所以用的时候要仔细分析用那种合适。
   2、 关于模式的选用问题           模式的选用要根据实际的业务需求,通过对业务逻辑的仔细分析,再根据模式具有的特性和应用场景进行合理的选择和区分。大部分情况下业务的场景决定了哪种模 式,而不是选择哪个模式去实现一个业务,少数情况几种模式确实都能解决问题,那主要就是考虑以后的扩展了。                   到这里我们已经了解了7种结构型模式,下一篇我们继续给大家介绍设计模式的行为型模式,先预览一下行为型模式的种类吧:

u模版方法模式

u命令模式

u迭代器模式

u观察者模式

u终结者模式

u备忘录模式

u解释器模式

u状态模式

u策略模式

u职责链模式

u访问者模式