深入PHP面向对象、模式与实践

1 语法

Java 24 设计模式

(前言)

1.1 基础语法

clone

需要操作原对象,但又不想影响原对象.

$K_back = clone $K;

基本数据类型和数组都为真复制,即为真副本,当属性为对象时,为假复制,改变副本仍会影响原对象.解决方案:

//在原对象中添加
function __clone(){
    $this->对象 = clone $this->对象
}

__clone在clone前自动触发,可以执行一些在备份前的属性操作.

&传递引用

方法引用传递,改变源对象

function set_K(& $K){...}

function & get_K(){...}

static延迟静态绑定

应用场景:Dog类和Person类都需要一个返回实例化的方法,Dog类和Person类都继承于Animal抽象类.

abstract class Animal{
    public static function create(){
        //实例化调用类
        return new static();
    }
}

class Person extends Animal{...}

//返回Person实例化类
Person::create();

拦截器

  1. __get($property),访问未定义的属性时调用.
  2. __set($property,$value),给未定义的属性赋值时被调用.
  3. __isset($property),对未定义属性调用isset()方法时调用.
  4. __unset($property),对未定义属性调用unset()方法时调用.
  5. __call($method,$arg_array),调用未定义方法时调用.__call很有用,但要慎用,因为太灵活.应用场景:有一个专门打印Person类信息的Person_Writer类,如果通过Person类调用Person_Writer类.

    //Person委托Person_Writer类处理打印事务.
    class Person {
        private $writer;
        ...
    
        function __call($method_name,$args){
            if(methood_exists($this->wirter,$method_name)){
                return $this->writer->$method_name($this);
            }
        }
    
        //高级__call写法,当委托方法参数不确定时使用.
        function __call($method_name,$args){
            //当然这里这样写法意义不大,但是call一般都是用call_user_func_array调用
            $args = $this ;
            if(methood_exists($this->wirter,$method_name)){
                return call_user_func_array(
                    array($this->writer,$method_name),$args);
                )
            }
        }
    
    }
    

回调函数

应用场景:
3个类,Product类,Product_Sale类,Product_Totalizer类,要实现:当卖出Product总共价格超过指定金额时,输出警告.

//Product
class Product {
    public $name;
    public $price;
}

//Product_Sale
class Product_Sale {
    private $callbacks;

    //记录回调函数
    function register_callback ($callback) {
        if(! is_callback($callback)){
            thow new Exception('callback not callable');
        }
        $this->callbacks[] = $callback;
    }

    //执行回调函数
    function sale ($product){
        print "{$product->name} : 处理中 n";
        foreach($this->callbacks as $callback){
            call_user_func($callback , $product);
        }
    }
}

//Produce_Totalizer
class Produce_Totalizer {
    static function warn_amount ($amt) {
        $count = 0;
        return function ($produce) use ($amt , &count) {
            $count += $produce->price;
            print " count : {count}n"
            if($count>$amt){
                print "quot;超过指定金额{$amt}啦~";
            }
        };
    }
}

//模拟场景
$product_sale = new Produce_Sale();
//指定报警金额为8块
$product_sale = register_callback(Produce_Totalizer::warn_amount(8)); 

//卖商品
$product_sale->sale(new Product("Durex",6));
$product_sale->sale(new Produce("Jissbon",5));

//输出结果
Durex : 处理中
    count :6 

Jissbon : 处理中 
    count: 11

超过指定金额8块啦~

get_class()instanceof

get_class(类)用于判断是否精准等于类名;

instanceof 可以判断是否其本身或继承于某父类.

类中的方法和类中的属性

get_class_methods('类名')澳门新浦京电子游戏,:获取类中所有方法.

get_class_vars('类名'):获取类中所有public参数;

反射API

一.策略模式(Strategy pattern)

设计模式和具体的语言没有关系,学习设计模式最重要的目的就是要建立面向对象的思想,尽可能地面向接口编程、低耦合、高内聚,使设计的程序可复用;

2 模式

    1.策略的共同点抽象成接口,具体策略实现这个接口;

在掌握面向对象的思想方式后,再回过头来看设计模式,就会有更深刻的理解;

2.1 组合

问题:课堂类被演讲类和研讨会类继承着.但是演讲类和研讨类都要实现一次性计费和上N次课计费的方法.和输出计算的方式.

解决方案1:
在课堂类中添加计算一次性付费的方法,上N次课的计费方法和输出计算方式的方法.

解决方案2: 运用组合,将处理计费和输出计算方式单独封装为一个计费策略类.

澳门新浦京电子游戏 1

abstract class Cost_Strategy {
    protected $duration;
    abstract function cost ();
    abstract function charge_type();

    public __construct($duration){
        $this->duration = $duration;
    }
}

class Timed_Const_Strategy extends Cost_Stratedy {
    function cost () {
        //上一次课给5块钱- -.
        return $this->duration * 5;
    }

    function charge_type(){
        return "多次课结算";
    }
}

class Fixed_Const_Strategy extends Cost_Stratedy {
    function cost (){
        return 30 ;
    }

    function charge_type(){
        return "一次性课结算";
    }
}

abstract class Leason {

    private $cost_strategy;

    public __construct(Const_Strategy $cost_strategy){
        $this->cost_strategy = $cost_strategy;
    }

    function __call($method_name,$args){
        $args = $cost_strategy ;
        if(methood_exists($this->cost_strategy,$method_name)){
            return call_user_func_array(
                array($this->writer,$method_name),$args);
            )
        }
    }
}

//运用
$leasons[] = new Seminar(new Timed_Const_Strategy(4));
$leasons[] = new Lecture(new Fixed_Const_Strategy(null));

foreach ($leasons as $leason){
    print "leason charge : {$leason->const()}";
    print "charge_type : {$leason->charge_type()}"
}

leason charge 20. charge_type : 多次课结算;
leason charge 30. charge_type : 一次课结算;

组合既委托.同级委托.

继承既父子关系.

    2.抽象一个策略接口的包装容器Context,里面包装策略的实现方法;

学习设计模式,一定要勤学多练;

3 生成对象

    3.策略的具体执行类,负责策略的最终应用;

(P4)

3.1 单例模式

确保系统中只有唯一一个用例.例如系统配置文件.

重点

1: 构造方法私有.

2: 类本身包含自己的实例化属性.

澳门新浦京电子游戏 2

class Preferences {
    private static $instance;
    private function __construct(){ ... }

    public static function get_instance(){
        if(empty(self::$instance)){
            self::$instance = new Preferences();
        }
        return self::$instance;
    }
    ...
}

//使用
$preferences = Preferences::get_instance();

    总结:

对象的创建会消耗掉系统的很多资源,所以单独对对象的创建进行研究,从而能够高效地创建对象就是创建型模式要探讨的问题;

3.2 工厂模式

通过一个父类,生产处多个不同功能的子类.

特点:产品方(新浪微博)和需求方(显示新浪微博)一一对应.

问题:印象笔记中,来源可能为新浪微博,或者开发者头条,在印象笔记显示的时候,两者的页眉和页尾是不一样的.

澳门新浦京电子游戏 3

   
策略模式体现高内聚低耦合的特点,把具体策略类,策略共同点抽象出的接口,及策略的执行场景(上下文),具体的策略执行类分开;

在解决了对象的创建问题之后,对象的组成以及对象之间的依赖关系就成了开发人员关注的焦点,因为如何设计对象的结构、继承和依赖关系会影响到后续程序的维护性、代码的健壮性、耦合性等。

3.3 抽象模式

RLGL!!!.印象笔记不只要显示新浪微博内容!!!还要显示我的新浪账号,还要该微博啊!!卧槽~憋着急,吻我.

工厂模式主要用于生产一一对应的产品方和需求方,而抽象模式要做的是一个需求方(印象笔记_显示新浪微博),要多个工厂(把需求方抽象为多个需求方),例如提供新浪内容的工厂,提供新浪账号的工厂.提供微博内容的评论的工厂等.

澳门新浦京电子游戏 4

代码:

abstract class Show_Evernote {
    abstract function get_header_text();
    abstract function get_context();
    abstract function get_footer_text();
    abstract function get_user();
    abstract function get_comment();

}

class 显示新浪微博 extends Show_Evernote{
    function get_header_text(){...};
    function get_context(){new 新浪微博_内容;}
    function get_footer_text(){...};
    function get_user(){new 新浪微博_账号 ;}
    function get_comment(){new 新浪微博_评论;}
}

//使用
印象笔记控件类->内容 = 显示新浪微博->get_context;
印象笔记控件类->账号 = 显示新浪微博->get_context;
...

二.代理模式(Proxy pattern)

对象结构的设计很容易体现出设计人员水平的高低;

3.4 平行模式

当使用工厂/抽象模式必须要制定具体的创建者(需求方).

平行模式和抽象模式的模型图一致,但代码实现不一样.

抽象模式中父类均为抽象类,而平行模式中,所以类都为普通类,方便父类的实例化.

在这里列出显示印象笔记类的实现代码

class Show_Evernote{
    private $内容;
    private $账号;
    private $评论;

    function __construct(内容,账号,评论){
        $this->内容 = 内容;
        $this->账号 = 账号;
        $this->评论 = 评论;
    }

    function get_内容(){
        return clone $this->内容);
    }

    function get_账号(){
        return clone $this->账号);
    }

    function get_评论(){
        return clone $this->评论;
    }
}

//使用
$factory = new Show_Evernote( 
    new 新浪微博内容(),
    new 新浪微博账号(),
    new 新浪微博评论()
);

印象笔记控件类->显示印象笔记 = $factory;

其实大家可以发现,原型模式只不过只在最顶层类中包装了一下各组件子类而已,然而这样可以轻松的组合他们,例如实现一个显示新浪微博内容,但要显示开发者头条账号的需求?

    1.定义一类事物共同的特点(能执行的操作)抽象成接口

在对象的结构和对象的创建问题都解决了之后,就剩下对象的行为问题了,如果对象的行为设计的好,那么对象的行为就会更清晰,它们之间的协作效率就会提高;

4 使用对象

    2.具体的事物类,实现这个接口,及具体执行的操作

(P5)

4.1 组合模式

组合模式,可以理解为单一对象管理组合对象(聚合组件),最终组合体下的各个组合部件最好类型一致.不然特殊性越多,需要判断就越多.

假设捶背男,洗脚男,洗发男,用来服务一个人(妹子).

假设妹子的几个部位可用的服务男均为无限个.

澳门新浦京电子游戏 5

//创建一个妹子
$妹子 = new 人();

//添加洗脚男、捶背男
$妹子->add_man(new 洗脚男);
$妹子->add_man(new 捶背男);

//循环所有男的给予舒服的方法.
$妹子->计算舒服程度();

这是一个很理想的组合模式,在现实情况,我们使用组合模式,可能不得不创建多种类型的洗脚男,需要添加许多判断条件.

    3.如果其中一个事物类不能做这些事,可以将其定义成其他事物类的代理

设计模式并不只是一种方法和技术,它更是一种思想、一个方法论。

4.2 装饰模式

装饰模式,首先洗脚男,洗发男,捶背男都是人,但是如果,一个男的又捶背,又洗发,这怎么玩?.add_man两次?这不科学吧,来给这些男的装饰一下吧~

澳门新浦京电子游戏 6

abstract class 人{
    ...
    abstract function get_well();
}   

class 男 extends 人 {
    //无论你是神马男,服务你,你就能获得10点舒服度.
    private $well = 10;
    function get_well(){
        return $this->well();
    }
}

abstract class 装饰男类型 extends 人 {
    protected $人;
    function __construct(人 $人){
        $this->人 = $人;
    } 
}

class 捶背装饰 extends 类型男装饰{
    function get_well(){
        return $this->人->get_well()+30;
    }
}

class 洗发装饰 extends 类型男装饰{
    function get_well(){
        return $this->人->get_well()+20;
    }
}

class 洗褪装饰 extends 类型男装饰{
    //老子不喜欢别人碰我的毛裤.
    function get_well(){
        return $this->人->get_well()-20;
    }
}

//创建捶背,能给予的舒服指数 - -嘻嘻.
$人 = new 捶背装饰(new 男);
$人->get_well(); // 10+30 = 40

//来来来,全能选手,捶背、洗发、洗腿一起来
$人 = new 洗脚装饰(new 洗发装饰(new 捶背装饰(new 男()))); //10+30+20-20 = 40,注意顺序,由里到外执行.

装饰模式,既(组合+继承),基类方法一定要尽量少,不然子类可能有它不该有的方法.直接类继承,她只可能是一种形态,而她的多种形态可能一并拥有的时候,应该运用组合.

继承即单一多态,组合既多种多态.

这个例子中,你可以添加女,然后把装饰男类型改为装饰通用类型,但每个get_well()都要多一个判断是男还是女(如果给予的舒服程度不一样).

这只是确保不可能出现在,之外的第三种人,如果基类为动物,给予服务的可能是鸡,鹅,鸭,那么装饰类型应该运用工厂模式,动物形态和装饰形态一一对应.方便拓展.

除了服务类型,服务男的样子也很重要,这就多了一种装饰,现在有装饰男类型相貌男类型,这种情况怎么破,其实类似.

澳门新浦京电子游戏 7

//如何获取捶背的帅哥麦?,
$人 =new 男类型(new 捶背(new 帅哥麦(new 男())));

   
4.定义成其他事物类的代理的类,要实现两个构造方法,一个是定义默认代理的事物类,一个是可以任意定义代理的事物类;实现以后的复用;

它和具体的语言没有关系,学习设计模式最主要的目的就是要建立面向对象的思想,尽可能地面向接口编程、低耦合、高内聚,使你设计的程序尽可能地复用;

4.3 外观模式

即给外部系统提供清晰接口

例如当Model层写得很混乱,但是里面的方法还能用,那我们的Controller层应该列举一些清晰的访问方法来供View层访问.外观模式,强调的是清晰的访问接口.

    总结:
代理模式体现java的多态特性,代理类接到任务交给其他被代理的类去完成,前提是代理类和被代理类实现同一个接口;

(P23)

5 执行任务

三.单例模式(Singleton pattern)

简单工厂模式又叫静态工厂方法模式,是所有工厂模式中最简单的一个;

5.1 策略模式

给类添加功能.对象要显式的调用它.

继续刚才的洗脚男和人的故事吧…你丫的爽完了要给钱吧?支付宝?微信?现金?

这个付款方式有多种,实现方法不应该放在类中,而是应该委托给别的类

abstract class 人 {

    protectd $支付方式;

    function set_支付方式(){...}

    function 付款(金额){
        return $支付方式->付款($金额);
    }
}

abstract class 付款{
    abstract function 付款($金额);
}

class 支付宝付款 extends 付款{

    function 付款($金额){
        return 外接支付宝付款流程($金额);
    }
}
...

//使用
$男 =new 男();

///爽爽爽
...

//结账
$支付宝支付账单 = new 支付宝付款($金额);
$人 = new 男();
$人->set_支付方式(new 支付宝付款());
$人->付款();

    1.类的构造方法是私有的

在程序设计中,离不开对象的创建,对象创建效率的高低对系统资源的占用影响较大,因此需要对此进行详细研究,最好的办法就是将对象的创建和使用分开;

5.2 观察者模式

当被观察者发生变化,观察者需要被通知.

当数据发生变化,页面需要被通知.

使用步骤:

  1. 观察者加载到被观察者中.
  2. 被观察者通知观察者.

澳门新浦京电子游戏 8

例如登陆类(被观察)状态改变,要出发邮件系统和日志系统(观察者)

interface 被观察者{
    function attach(观察者);
    function detatch(观察者);
    function notify();
}

class Login implements 被观察者{
    private $观察者;

    function __construct(){
        $this->观察者 = array();
    }

    function attach($观察者){
        $this->观察者 = $观察者; 
    }

    function detach($观察者){
        //删除某个观察者的操作;
    }

    function notify(){
        foreach ($this->观察者 as $单个观察者){
            $单个观察者->update($this);
        }
    }       
}

interface 观察者{
    function update(被观察者);
}

abstract class Login_观察者 implements 观察者{
    private $login;
    function __construct (Login $login){
        $this->login = $login;
        $login->attach($this);
    }

    function update(观察者 $观察者){
        if ($观察者 ===$this->login){
            $this->do_update($观察者);
        }
    }
    abstract function do_update(Login $login);
}

class 邮件观察者 extends 登陆观察者 {
    function do_update(Login $login){
        //判断条件 发送邮件
    }
}

class 日志观察者 extends 登陆观察者 {
    function do_update(Login $login){
        //判断条件 记录到日志;
    }
}

//使用
$login = new Login();
new 邮件观察者 ($login);
new 日志观察者 ($login);

PHP有内置的SPL实现上述的观察者模式.

    2.类中有一个获取类实例的静态方法

简单工厂模式只适用于要创建的具体对象比较少或简单的情况,而创建比较复杂的对象时,简单工厂模式不是一个很好的选择;

5.3 访问者模式

问题:
在一个军队中,有很多军队,军队下面可能包含军队/步兵/弓箭手,这时我们要显示一个军队的战斗力/需要粮食的各级分配?(遍历对象并设置显示方法).怎么办?.解决办法是军队还是保存自己的基本信息,设置一个访问者,访问者包含总战斗力方法和总粮食的方法.

澳门新浦京电子游戏 9

访问者

abstract class 军队访问者{
    abstract function 访问(单元);

    function 访问军队($军队){
         $this->访问($军队);
    }
    function 访问弓箭手($弓箭手){
        $this->访问($弓箭手);
    }

    //这里重复定义了大量代码,其实可以用call来替代
    function __call($method_name,$args){
        if(strrpos($method_name, "访问")){
            return call_user_func_array(
                array($this,"访问"),$args
            );
        }
    }
}

class 军队战斗力访问者 extends 军队访问者{
    private $text="";

    function 访问($单元){
        $ret = "";
        $pad = 4*$单元->getDpth(); //设置显示深一级前面多4个空格.
        $ret .= sprintf( "%{$pad}s","");
        $ret .= get_class($单元). ": ";
        $ret .= "战斗力: " .$单元->bombardStrenth()."n";
        $this->text .=$ret;
    }

    function get_text(){
        return $this->text;
    }
}

被访问者

abstract class 单元{
    function 接受($军队访问者){
        $method = "访问_".get_class($this);
        $军队访问者->$method($this);
    }

    private $depth;
    protected function set_depath($depth){
        $this->depth=$depth;
    }

    function get_depth(){
        return $this->depth;
    }
    ...
}

abstract class 综合单元 extends 单元{
    function 接受($军队访问者){
        parent::接受($军队访问者)
        foreach($this->单元集合 as $this_unit){
            $this->unit->接受($军队访问者);
        }
    }
}

class 军队 extends 综合单元{
    function bombardStrenth(){
        $ret =0;
        foreach($this-units() as $unit){
            $ret += $unit->bombardStrenth();
        }
        return $ret
    }
}

class 弓箭手 extends 单元{
    function bombardStrenth(){
        return 4;
    }
}

调用

$main_army = new Army();
$main_army->add_unit(new 步兵());
$main_army->add_unit(new 弓箭手());

$军队战斗力访问者_实例 =new 军队战斗力访问者();
$main_army->接受(均分战斗力访问者);
print $军队战斗力访问者->get_text();

输出

军队: 战斗力: 50
    步兵: 攻击力 :48
    弓箭手: 攻击力: 4

    3.多线程情况还要考虑线程同步获取实例的方法

(P31)

5.4 命令模式

例子为Web页面的login和feed_back,假如都需要使用ajax提交,那么问题来了,将表单封装好提交上去,得到了返回结果.如何根据返回结果跳转不同的页面?.

有些同学就说了,login和feed_back各自写一个方法憋,提交的时候调用各自的方法.

然后再来个logout命令..增加..删除..命令怎么办..

命令模式比较适合命令执行例如登陆,反馈等简单只需要判断是否成功的任务

澳门新浦京电子游戏 10

命令:

abstract class Command{
    abstract function execute(Conmmand_Context $context);
}

class Login_Command extends Command{
    function execute(CommandContext $context){
        $managr =Register::getAccessManager();
        $user = $context->get("username");
        $pass = $context->get('pass');
        $user_obj = $manager->login($user,$pass);
        if(is_null($user_obj)){
            $context->setError($manager->getError());
            return false;
        }
        $context->addParam("user",$user_obj);
        return true;
    }
}

部署命令的调用者

class Command_Facotry{
    public function get_command($action){
        $class = UCFirst(strtolower($action))."_Command";
        $cmd = new $class();
        return $cmd;
    }

}

客户端

class Controller{
    private $context;
    function __construct(){
        //Command_Context主要用来存储request和params
        $this->context =new Command_Context();
    }
    function process(){
        $cmd Command_Factory::get_commad($this->context->get('action'));
        if(!$cmd-execute($this->context)){
            //错误处理
        }else{
            //成功 分发视图
        }
    }
}

使用

$controller =new Controller();
$context = $controller->get_context();
$context->add_param('action','login');
$context->add_param('username','404_k');
$context->add_param('pass','123456');
$controller->process();

    总结:

工厂方法模式相对于简单工厂模式来说,就是把一个单一的工厂类,分成了多个具体的小工厂,并抽象出了一个工厂类,这个工厂类只负责定义创建的方式,创建的具体内容由继承它的小工厂类实现;

    单例模式用在要且只要一个对象的类,保证类实例的唯一性;

(P35)

四.多例模式(有上限的多例模式)(Mulition pattern)

【工厂方法模式】

    1.类的构造方法是私有的

[定义]
工厂方法模式中抽象工厂类负责定义创建对象的接口,具体对象的创建工作由实现抽象工厂的具体工厂类来完成;

    2.把获取实例的方法放在静态代码块中,把获取的实例放在List中

[原理]
工厂方法模式主要由4部分组成:抽象工厂类、实现抽象工厂类的具体工厂类、抽象类和实现抽象类的具体类;

    3.多线程情况很少

[使用时机]
当在代码中使用new来创建对象时,就可以考虑使用工厂方法模式。一般来说,工厂方法模式会应用在比较复杂的对象创建上;

    总结:

[优点]

    多列模式,但实例的数量有个上限,需要根据具体情况设置。

在工厂方法模式中,客户端不再负责对象的创建,而是把这个责任交给了具体的工厂类,客户端只负责对象的调用,从而明确了各个类的职责;

五.简单工厂模式(Simple factory pattern)

如果有新的产品加进来,只需要新增加一个具体的创建产品工厂类和具体的产品类就可以了,不会影响到原来已有的其他代码,代码量也不会变大,后期维护更加容易,增强了系统的可扩展性;

    1.把要产生的类的共性抽象成一个接口

[缺点]
使用该模式需要额外地编写代码,增加了工作量;

    2.具体的类实现这个接口

(P39)

    3.写一个产生类的工厂方法

每一种设计模式都是为了解决一类问题而产生的,如果说工厂方法模式是针对一个产品结构而言的话,那么抽象工厂模式就是针对于多个产品结构而言的,它主要用于帮助客户一次创建多个不同产品对象;

    4.写一个利用工厂制造类的操作者类

(P40)

    总结:

工厂方法模式是针对于一个产品系列的,而抽象工厂模式是针对于多个产品系列的,即工厂方法模式是一个产品系列一个工厂类,而抽象工厂模式是多个产品系列一个工厂类;

   
简单工厂模式简化了生成具体对象,我们所需要的只需要修改对象对应的类的共同属性就可以实现,工厂类完成其他操作

(P45)

六.抽象工厂模式(Abstract factory pattern)

【抽象工厂模式】

    1.定义最终目标类的共性成一个接口

[定义]
如果客户端需要创建一些产品结构,而这些产品结构又分别属于不同的产品类别,则可以使用抽象工厂模式,抽象工厂模式中抽象工厂类负责定义创建对象的接口,这一系列对象的创建工作由实现抽象工厂的具体工厂类来具体完成;

    2.定义要最终目标类的抽象类,实现目标类共性定义的接口

[原理]
抽象工厂模式主要由4部分组成:抽象工厂类、实现抽象工厂类的具体工厂类、抽象类和实现抽象类的具体类;

    3.定义目标类中几个不同的属性的具体执行类继承对应的抽象类

[使用时机]
当系统需要创建一系列相互关联的对象时,就需要使用抽象工厂模式;

    4.根据工厂类最终要实现的方法,定义一个工厂类接口

[优点]

    5.定义一个包含通用方法的抽象类,实现工厂类接口

在抽象工厂模式中,客户端不再负责对象的创建,而是把这个责任交给了具体的工厂类,客户端只负责对对象的调用,从而明确了各个类的职责;

    6.根据具体需要,创建具体实现工厂类,继承抽象类

当一系列相互关联的产品被设计到一个工厂类里后,客户端的调用将会变得非常简单,而且,如果要更换这一系列的产品,只需要更换一个工厂类即可;

    总结:

[缺点]
如果有新的产品加进来,则需要修改抽象工厂类的设计,并同时修改实现这个抽象工厂类的具体工厂类,需要额外编写代码,增加了工作量;

   
工厂模式符合开闭原则(OCP),在产品中增加一类产品,再增加一个工厂方法就可以了。

(P52)

七.门面模式(Facade pattern)

在简单工厂模式中,一个工厂类负责所有产品对象的创建,这个工厂类的职责大大增加。由于简单工厂模式使用静态方法来创建对象,这就导致静态方法无法被继承;

    1.根据处理某类事件的具体操作定义一个接口

在工厂方法模式中,一个具体的工厂类负责创建一个单独的产品,如果有两个不同的产品要创建,就需要两个不同的工厂类,即使这两个产品有某些必要的联系,也还是需要两个不同的工厂类;

    2.定义一个具体实现上面接口的实现类

在抽象工厂模式中,一个具体的工厂类负责创建一系列相互关联的产品,当一系列相互关联的产品被设计到一个工厂类里后,客户端的调用将会变得非常简单;如果要更换这一系列的产品,则只需要更换一个工厂类即可;

    3.定义一个类把所有操作封装在一个方法里

(P57)

    4.定义一个需要这些操作的客户端

【创建者模式】

    总结:

[定义]
创建者模式就是将一个复杂对象的构建与它的表示相分离,使得同样的构建过程可以创建不同的表示,而且客户端不知道对象的构建细节;

   
门面模式把一些复杂的操作封装起来,让用户感觉操作起来十分简便。如果操作流程改了,只需要改少量代码即可达到需求。

[原理]
创建者模式主要由5个部分组成:组装类、抽象创建者类、实现抽象创建者类的具体创建者类、抽象产品类和实现抽象产品类的具体产品类;

八.适配器模式(Adapter pattern)

[使用时机]
当系统需要创建一个复杂的对象,而且这个复杂的对象组装起来比较麻烦时,就可以使用创建者模式;

    1.定义一个适配者所有的操作接口

[优点]
在创建者模式中,客户端不再负责对象的创建和组装,而是把这个创建的责任交给具体的创建者类,把组装的责任交给组装类,客户端只负责对象的调用,从而明确了各个类的职责;

    2.定义一个类实现这个适配者操作接口

[缺点]
虽然利用创建者模式可以创建出不同类型的产品,但如果产品之间的差异非常大,则需要编写多个创建者类才能实现,这时如果结合工厂模式更好;

    3.定义一个被适配者所有操作的接口

(P64)

    4.定义一个类实现被适配者操作接口

【原型模式】

    5.定义一个连接两者的适配器

[定义]
原型模式就是通过一个原型对象来表明要创建的对象类型,然后用复制这个原型对象的方法来创建更多同类型对象;

    6.定义一个需要具体操作的类

[原理]
原型模式主要由2部分组成:抽象原型和具体原型类;

    总结:

[使用时机]
当系统需要创建的对象是动态加载的,而且产品具有一定层次时,需要使用原型模式;

   
适配器模式不适合在设计阶段使用,主要应用在扩展修改系统功能时使用,使用适配器模式减少原有系统代码修改。

[优点]
在原型模式中,可以动态地添加产品类,而且对整体结构没有影响;

九.模板模式(Template pattern)

[缺点]
由于原型模式需要给每个类都配备一个克隆的方法,这就需要在设计类时通盘考虑,因为在已有类的基础上来添加clone操作是比较困难的;而且原型模式在实现深层次的复制时,需要编写一定量的代码;

    1.定义一个顶级抽象类模型,包含基本方法和模板方法(基本方法的组合)

(P73)

    2.定义具体的对象模型类,继承顶级抽象模型类

【单例模式】

    3.定义执行操作类

[定义]
单例模式就是确保一个类只有一个实例,并且该实例必须自动创建,并向整个系统提供该实例;

    总结:

[原理]
单例模式可以分为饿汉式单例模式和懒汉式单例模式两种:饿汉式单例模式在类初始化时就已经创建了自身的对象,而懒汉式单例模式则是在需要使用的时候才创建自身的对象;

   
模板方式在(struts2)应用挺多,提供一个抽象类,然后修改其protected方法,实现具体的功能,然后调用一个execute方法,完成操作。父类调用子类的方法,在项目中不建议使用

[使用时机]
当系统要求一个类只有一个实例时,就需要使用单例模式;

十.建造者模式(Builder pattern)

[优点]
在单例模式中,客户调用类的实例时,只能调用一个公共的接口,这就为整个开发团队提供了一个共享的概念;

    1.前一部分如同模板模式

[缺点]
实现单例模式的类在实例化后,不能被别的类继承;在分布式系统中,当系统中的单例类被复制运行在多个虚拟机下时,在每一个虚拟机下都会创建一个实例对象,此时如果想知道具体哪个虚拟机下运行着哪个单例对象时很困难的,而且单例类很难实现序列化;

    2.根据实际需求创建一个建造者抽象类,定义建造者用到的方法

(P79)

    3.根据具体需要被造的物体设计对象制造者类,继承建造者抽象类

在所有的设计模式中,最简单的就数外观模式了。外观模式又叫门面模式,顾名思义,就是对一个系统进行包装,该系统对外的接口统一由外观类进行提供;

    4.根据客户最终需要设计指导者类,确定具体建造流程

【外观模式】

    5.定义执行操作类

[定义]
外观模式就是为子系统对外提供的一组接口提供一个统一的界面,使得其他系统对该系统的访问都通过这个统一的界面来完成;

    总结:

[原理]
外观模式主要由3部分组成:抽象外观类、实现抽象外观类的具体外观类和其他子系统;

   
建造者模式适合产品类复杂繁多的情况,就是业务较复杂时。还有对象在创建过程中使用到其他不易得到的对象。

[使用时机]
当一个复杂的系统需要对外提供接口时,就需要将对外提供的接口统一封装在一个外观类里,供外系统使用;

十一.桥梁模式(Bridge pattern)

[优点]
外观模式通过提供一个统一的对外接口,避免了外部系统和子系统之间的直接联系,从而降低了系统间的依赖和复杂度;

    1.定义一个最终目的产品抽象类,包含对应的抽象操作方法

[缺点]
限制了外部系统对子系统调用的灵活性,只能按照外观类中提供的方式对子系统进行调用;

    2.定义最终产品类,继承上面的目的产品抽象类,重写抽象操作方法

(P107)

    3.定义一个负责生产最终目的产品的抽象公司类,包含公司类要实现的方法

【适配器模式】

    4.定义生产具体产品的公司类继承抽象公司类,重写抽象公司类的方法

[定义]
适配器模式就是将一个系统的接口转换成另外一种形式,从而使原来不能直接调用的接口变得可以调用;

    5.定义执行操作类

[原理]
适配器模式主要由3部分组成:目标类、源类和适配器类;

    总结:

适配器类分为对象适配器和类适配器:对象适配器即实现目标类的接口,依赖适配者类;类适配器即直接继承适配者类,并实现目标类的接口;

   
桥梁模式的优点是类间解耦,使类间的联系弱化。业务抽象角色引用业务实现角色

[使用时机]
当系统需要使用一个外部接口,而这个外部接口不符合系统需要的时候,就需要使用适配器模式;

十二.命令模式(Command pattern)

[优点]
使用适配器模式,可以将一个系统的接口和本来不相容的另一个系统的类联系起来,从而使得这两个类能够在一起工作,强调了对接口的转换;

    1.根据命令的最终接受者执行的操作,定义抽象命令接收者命令

[缺点]
对于类适配器来说,不能适配一个类以及它的子类;对于对象适配器来说,重新定义被适配的类的行为比较困难;

    2.定义命令接收者类,继承上面定义的抽象类

(P122)

    3.定义命令抽象类,包含一个execute方法

【代理模式】

    4.定义具体的命令类

[定义]
代理模式就是给一个对象提供一个代理对象,由这个代理对象控制对原对象的引用,使代理类在客户端和原对象之间起到一个中介的作用;

    5.定义命令调度者类,接收命令并布置具体执行

[原理]
代理模式主要由3部分组成:抽象目标类、具体目标类和代理类;

    总结:

[使用时机]
当系统需要对某个对象进行额外的控制时,就需要使用代理模式;

   
命令模式具备很好的封装性,把命令的请求方和执行方分开了。但如果业务逻辑复杂,Command类会过于复杂,在实际项目中类会膨胀。

[优点]
使用代理模式,能够在不改变原来代码功能的基础上对某一对象进行额外的控制,从而更好地体现了面向对象中单一职责的思想;

十三.装饰模式(Decorator pattern)

[缺点]
对于静态代理来说,一个接口只服务于一种类型,如果要代理的方法很多,则要为每个方法定义一个接口;

    1.定义一个最原始的,被修饰的核心类

(P153)

    2.定义具体的被装饰的具体类,继承核心类

【装饰模式】

    3.定义一个装饰抽象类

[定义]
装饰模式就是使用被装饰类的一个子类的实例,在客户端将这个子类的实例委托给装饰类。装饰模式是继承关系的一个替代方案;

    4.根据具体的被装饰类,定义装饰类

[原理]
装饰模式主要由4部分组成:抽象的被装饰类、具体的被装饰类、抽象的装饰类和具体的装饰类;

    5.定义要查看最终被装饰产品的类

[使用时机]
当系统需要扩展一个类的功能,或者客户端需要动态的给一个对象添加功能,并且此时如果使用继承会很复杂时,就需要使用装饰模式;

    总结:

[优点]
使用装饰模式,能够提供比使用继承关系更灵活的扩展对象的功能,它可以动态地增加对象的功能,并且可以随意地组合这些功能;

    装饰模式是对继承的补充,装饰模式是给类动态的增加功能

[缺点]
正因为可以随意地组合这些功能,有时候就可能会出现一些不合理的逻辑。使用装饰模式进行系统设计往往会产生许多看上去类似的小对象,这些对象的类或是它们的属性值是一样的,仅仅在它们相互连接的方式上有所不同;

十四.迭代器模式(Iterator pattern)

(P171)

    1.定义一个具体项目迭代器接口继承java.util.Iterator类

桥模式可能是所有设计模式中最难理解的一个模式了;

    2.定义一个项目使用的迭代器类,实现上面那个接口

桥模式中的抽象指的是抽象类及派生类,实现指的是这些抽象类及派生类实现自己的方式;

    3.定义有一个对项目的操作接口

【桥模式】

    4.定义具体的项目操作类实现上面的接口

[定义]
桥模式就是将抽象与其实现解耦,使它们可以分别独立地变化。桥模式也是继承关系的一个替代方案;

    5.定义项目的最终操作类

[原理]
桥模式主要由4部分组成:抽象类、抽象类的继承类、实现类和实现类的继承类;

    总结:

[使用时机]
当系统需要在它的抽象和实现之间增加更多的灵活性,或者一个对象有多于一个的抽象和实现时,就需要使用桥模式;

   
Java已经把迭代器给我们准备好了,项目很少再独立写迭代器了,直接用List或Map就可以解决问题。

[优点]
使用桥模式,能够提供比使用继承关系更灵活的功能。它可以使抽象和实现分离开,降低了耦合关系。当有新的抽象或实现方式时,只需要继承一个抽象和继承一个实现即可;

十五.组合模式(Compsite pattern)

[缺点]
如果要重新抽象出另外一个类型,则需要修改抽象;

    1.定义一个实现所需功能的抽象类

(P196)

    2.根绝具体所需生成的对象定义类,继承上面的抽象类

适配器、代理、装饰和桥模式都是为了解决对象之间的依赖关系,降低对象间的耦合度,给设计增加更多的灵活性;

    3.定义跟节点类,及接口

(P201)

    总结:

【组合模式】

   
组合模式分为安全模式和透明模式,提供一套组合方法,适合树形结构或者局部与整体的关系。

[定义]
组合模式就是把部分和整体的关系用树形结构来表示,从而使客户端能够把一个一个的部分对象和由它们组合起来的整体对象采用同样的方式来看待。它也是继承的一个替代方案;

十六.观察者模式(Observer pattern)

[原理]
组合模式主要由3部分组成:抽象类、叶子类和父类;

    1.定义一个被观察者实现Observable接口。

[使用时机]
当系统要描述的对象是树形结构,并且要在客户端忽略掉子对象和父对象的区别时,就需要使用组合模式;

    2.观察者实现Observer接口。

[优点]
使用组合模式,能够提供比使用继承关系更灵活的功能,并且可以灵活地组合子对象和父对象之间的关系,从而使客户端的调用简单,客户端可以一致地使用组合结构或其中单个对象,用户就不必关心自己处理的是单个对象还是整个组合结构,这简化了客户端代码;

    3.操作类实现具体操作

[缺点]
虽然组合模式使得向集合添加新类型的组件变得容易,只要这些组件提供一个相似的编程接口即可,但这也是它的缺点,因为这种做法很难限制某个类;

    总结:

(P220)

    观察者和被观察者自己没有任何联系,方便以后的扩展和维护。

【享元模式】

十七.责任链模式(Chain of Responsility pattern)

[定义]
享元模式就是把部分和整体的关系用树形结构来表示,从而使客户端能够把一个一个的部分对象和由它们组合起来的整体对象采用同样的方式来看待。它也是继承的一个替代方案;

    1.根据请求对象的属性,定义一个接口

[原理]
享元模式主要由3部分组成:享元类、具体的享元类和享元工厂类;

    2.定义具体的请求对象类

[使用时机]
当系统需要使用大量重复的对象,而且这些对象要消耗很大的内存时,就需要使用享元模式;

    3.根据请求处理者类抽象一个Handler类

[优点]
使用享元模式,可以为系统节省大量的空间;

    4.定义具体的请求处理者类,继承上面的抽象类

[缺点]
但是在享元工厂里可以看到,需要维护所有的享元对象,如果要维护的享元很多,在查找的时候要消耗大量的时间。因此,享元模式是典型的以时间换空间;

    总结:

(P227)

   
责任链模式是请求者不必考虑谁是处理者,只要请求发出,就会有处理者,Handler的子类可以继续增加下去。调用者不用了解变化过程。

结构型模式主要解决了对象的组成以及对象之间的依赖关系;

十八.访问者模式(Visitor pattern)

(P234)

    1.定义一个访问者超级接口

【模板方法模式】

    2.定义具体的访问者接口,包含访问者要实现的方法

[定义]
模板方法模式就是定义一个算法执行的骨架,而将具体的算法延迟到子类中来实现;

    3.定义具体的访问者实现类

[原理]
模板方法模式主要由2部分组成:抽象的骨架类和具体实现类;

    4.定义被访问的对象的抽象类

[使用时机]
当系统中算法的骨架是固定的,而算法的实现可能有很多种的时候,就需要使用模板方法模式;

    5.实现具体的被访问对象继承上面的类

[优点]
使用模板方法模式,在定义算法的骨架的同时,可以很灵活地实现具体的算法,满足用户灵活多变的需求;

    6.执行操作类

[缺点]
虽然使用模板方法模式可以很自由地实现具体的算法,但如果算法的骨架有改变的话,则需要修改抽象类;

    总结:

(P270)

   
访问者模式是访问者对象和被访问者对象隔离,根据访问者模式能实现简单的Interceptor

【观察者模式】

十九.状态模式(State pattern)

[定义]
观察者模式就是定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新;

    1.定义要表示的对象抽象出来的接口

[原理]
观察者模式主要由4部分组成:抽象目标类、具体目标类、抽象观察者类和具体观察者类;

    2.定义具体的对象类,实现上面定义的接口

[使用时机]
当对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有所改变,或者说当一个对象必须通知其他对象,而它又不能假定其他对象是谁的时候,就需要使用观察者模式;

    3.定义对象状态的抽象类

[优点]
观察者模式在被观察者和观察者之间建立一个抽象的耦合,每一个具体观察者都符合一个抽象观察者的接口,被观察者并不知道任何一个具体观察者,它只知道它们都有一个共同的接口,从而使得被观察者和观察者耦合度降低,而且观察者模式支持广播通信。被观察者会向所有的注册过的观察者发出通知;

    4.定义对象处于的环境上下文类

[缺点]
如果一个被观察者对象有很多观察者,通知所有的观察者会耗费很多时间;

    5.定义对象会处于的具体状态类

(P286)

    6.客户端执行类

【状态模式】

    总结:

[定义]
状态模式就是根据对象的状态不同,将有不同的行为;

   
状态模式,当一个对象内在状态改变时,允许其改变行为,看起来好像对象对应的类发生了改变,但子类会太多。

[原理]
状态模式主要由3部分组成:抽象状态类、具体状态类和上下文类;

二十.原型模式(Prototype pattern)

[使用时机]
当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为,或者一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态时,就需要使用状态模式;

    1.写一个要被创建的对象类的模板类

[优点]
状态模式使代码中复杂而冗长的逻辑判断语句问题得到了解决,而且具体状态角色将具体状态和它对应的行为封装了起来,这使得增加一种新的状态变得十分简单;

    2.定义要被clone的对象类

[缺点]
使用状态模式时,每个状态对应一个具体的状态类。使结构分散,逻辑不太清晰,阅读代码时比较困难;

    3.客户端执行操作

(P299)

    总结:

【策略模式】

   
原型模式是对通过对已有对象的拷贝创建新对象。既能节约硬件资源也能有效保护元对象不被修改。

[定义]
策略模式就是定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化;

二十一.中介者模式(Mediator pattern)

[原理]
策略模式主要由3部分组成:抽象策略类、具体策略类和上下文场景类;

    1.定义抽象的中介者类,具体的中介者去继承这个类

[使用时机]
当系统需要能够在几种算法中快速地切换,或系统中有一些类,它们仅行为不同时,或系统中存在多重条件选择语句时,就可以考虑采用策略模式;

    2.定义中介这要协调的同伴类

[优点]
使用策略模式,可以替换继承关系的办法,也可以避免使用多重条件转移语句;

    3.定义要执行具体操作的类

[缺点]
使用策略模式时客户端必须知道所有的策略类,并自行决定使用哪一个策略类,如果算法比较多,则会造成很多的策略类;

    4.客户端调用执行

(P305)

    总结:

【职责链模式】

    中介者模式实现很好的封装,是类间的依赖减少。

[定义]
职责链模式就是使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止;

二十二.解释器模式(Interpreter pattern)

[原理]
职责链模式主要由2部分组成:抽象处理类、具体处理类和上下文场景类;

    1.声明一个表达式都要实现的抽象类,包含interpret()方法

[使用时机]
当有多个对象处理请求,而且需要动态指定处理者的时候,就可以考虑使用职责链模式;

    2.具体的表达式类,继承上面的抽象类,实现具体的方法

[优点]
使用职责链模式可以降低类之间的耦合度。使得处理类仅需要保持一个指向其后继者的引用,也使得一个对象无需知道是其他哪一个对象处理其请求。对象仅需知道该请求会被正确地处理,接收者和发送者都没有对方的明确的信息,这种处理方法增强了给对象指派职责的灵活性;

    3.表达式运行的上下文环境类

[缺点]
因为对象不知道是其他哪一个对象处理其请求的,所以职责链模式不保证对象被接收,从而使得该请求可能一直到链接的末端都得不到处理;

    4.客户端调用执行类

(P316)

    总结:

【命令模式】

   
定义一个语句,解释器模式可以定义出其文法的一种表示,并提供一个解释器,客户端可以利用解释器来解释这个语句。

[定义]
命令模式就是把一个请求或者操作封装到一个对象中。命令模式允许系统使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能;

二十三.亨元模式(Flyweight pattern)

[原理]
命令模式主要由4部分组成:抽象命令类、具体命令类、调用者和接收者;

    1.定义只有内蕴状态的类,内蕴状态不会改变可以共享

[使用时机]
当需要先将一个函数登记上,然后在以后调用此函数时,就需要使用命令模式,其实这就是回调函数;

    2.定义含有外蕴对象的类,外蕴状态会随环境改变而改变,不可以共享

[优点]
命令模式把请求一个操作的对象与知道怎么执行一个操作的对象分割开,从而使新的命令可以很容易地被加入到系统里,可以很方便地设计命令队列,也可以很容易地实现对请求的撤销和重做,由于加进新的具体命令类不影响其他的类,因此增加新的具体命令很容易;

    3.单纯亨元模式和符合亨元模式

[缺点]
使用命令模式会导致某些系统有过多的具体命令类,这使得太多的命令类难于管理;

    总结:

(P342)

   
亨元模式可以有效降低内存中对象的数量,但会使系统变复杂,将亨元对象的状态外部化,读取外部状态的时间会变长。

【访问者模式】

二十四.备忘录模式

[定义]
访问者模式表示一个作用于某对象结构中的各元素的操作。它使开发者可以在不改变各元素的类的前提下定义作用于这些元素的新操作;

    1.定义一个备忘录类接口,起标识作用

[原理]
访问者模式主要由5部分组成:抽象访问者类、具体访问者类、抽象元素类、具体元素类和对象结果类;

    2.定义一个备忘录类,保存状态信息

[使用时机]
当一个对象结构包含很多类对象,它们有不同的接口,而系统要求这些对象实施一些依赖于其具体类的操作时,就可以考虑使用访问者模式;

    3.定义一个发起者,负责改变状态

[优点] 
使用了访问者模式以后,对于原来的类层次增加新的操作,仅仅需要实现一个具体访问者角色就可以了,而不必修改整个类层次,而且每个具体的访问者角色都对应于一个相关操作,因此如果一个操作的需求有变,就不用改动整个类层次,只需修改一个具体访问者角色即可;

    总结:

[缺点]
如果系统中的类层次发生了变化,就必须修改访问者角色和每一个具体访问者角色,因此访问者模式不适合具体元素角色经常发生变化的情况;

   
备忘录模式,可以有效恢复对象的状态。在不破坏封装的前提下,将对象的状态捕捉。

(P348)

Java 6个设计原则

【调停者模式】

一.单一职责原则

[定义]
调停者模式就是用一个中介者对象来封装一系列的对象交互。中介者使各对象不需要显示地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互;

   
接口做到单一职责,一个接口定义一类方法属性,类尽量只有一个原因引起变化。

[原理]
调停者模式主要由4部分组成:抽象调停者类、具体调停者类、抽象同事类和具体同事类;

二.里氏替换原则

[使用时机]
当一组对象以定义良好但是复杂的方式进行通信,而且这些对象产生的相互依赖关系结构混乱且难以理解,或一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象时,就可以考虑使用调停者模式;

   
里氏替换原则目的是加强程序的健壮性,在版本升级时可以做到非常好的兼容性,一个类的修改,不会影响其他类的正常运行。

[优点]
当使用调停者模式时,它将各个通信的对象进行解耦,使得原本分布于多个对象间的行为集中在一起,改变这些行为只需生成的协调类即可,这样的关系使得程序更易于理解、维护和扩展;

三.依赖注入原则

[缺点]
调停者模式使控制集中化,因为所有的交互行为都集中在调停者类中,使得这个类变得庞大而复杂,从而难以维护;

   
把各个类间的依赖关系交给第三方进行管理,类间没有直接的依赖关系,或依赖很弱,调用者不需要考虑类之间的依赖关系,后期对项目的维护也会相对简单。这样这些就实现了高内聚低耦合。

(P355)

四.接口隔离原则

【备忘录模式】

   
根据接口隔离原则拆分接口时,必须满足单一职责原则。接口隔离原则实现高内聚,并可提供定制化服务

[定义]
备忘录模式就是一个保存另外一个对象内部状态复制的对象,这样以后就可以将该对象恢复到原先保存的状态;

五.迪米特法则

[原理]
备忘录模式主要由2部分组成:源发器类和备忘录类;

   
出现在成员变量,方法的输入输出参数中的类被称为成员朋友类,一个类只和朋友类交流。

[使用时机]
当系统必须要保存一个对象在某一个时刻的状态,以方便需要时能恢复到先前的状态时,就需要考虑使用备忘录模式;

   
对外公布尽量少的public方法和非静态的public变量,尽量内敛,如果能设置成最终不可改变,尽量就加上final关键字,如果方法放在本类中,既不增加类间关系,对本类不产生负面影响,就放置在本类中

[优点]
使用备忘录模式,可以避免暴露一些只应由源发器管理却又必须存储在源发器之外的信息,而且能够在对象需要时恢复到先前的状态;

六.开闭原则

[缺点]
使用备忘录可能代价很高。如果源发器在生成备忘录时必须复制并存储大量的信息,或者客户非常频繁地创建备忘录和恢复源发器状态,可能会导致非常大的开销;

   
一个软件应该对扩展开放,对修改关闭,保证模块在不被修改的前提下被扩展。满足可复用和易维护,核心是抽象一个事物。

(P358)

【迭代器模式】

[定义]
迭代器模式提供一种顺序访问一个聚合对象中各个元素,而又不需暴露该对象的内部表示的方法;

[原理]
迭代器模式主要由4部分组成:迭代器角色、具体迭代器角色、容器角色和具体容器角色;

[使用时机]
当访问一个聚合对象的内容而无需暴露它的内部表示,或者需要支持对聚合对象的多种遍历,或者需要为遍历不同的聚合结构提供一个统一的接口时,就可以考虑使用迭代器模式;

[优点]
迭代器模式分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据;

[缺点]
迭代器模式是为容器而存在的,因此它仅适用于对容器的访问;

(P385)

【解释器模式】

[定义]
解释器模式给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子;

[原理]
解释器模式主要由4部分组成:抽象表达式角色、终结符表达式角色、非终结符表达式和上下文;

[使用时机]
当有一个语言需要解释执行,并且可将该语言中的句子表示为一个抽象语法树时,就可以考虑使用解释器模式;

[优点]
解释器模式提供了一个简单的方式来执行语法,而且容易修改或者扩展语法,但解释器模式比较适用于文法简单并且对处理的效率要求较低的情况;

[缺点]
对于复杂的文法,解释器模式将不是一个很好的选择,因为复杂文法的类层次变得庞大而无法管理,此时语法分析程序生成器这样的工具是更好的选择,它们无需构建抽象语法树即可解释表达式,这样可以节省空间而且还可能节省时间;

(P387)

【单一职责原则
(SRP)】

单一职责原则的核心思想就是:系统中的每一个对象都应该只有一个单独的职责,而所有对象所关注的就是自身职责的完成。它的英文缩写是
SRP,英文全称是 Single Responsibility Principle。

其实单一职责原则的意思就是:开发人员经常说的“高内聚、低耦合”。也就是说,每个类应该只有一个职责,对外只能提供一种功能,而引起类变化的原因应该只有一个。在设计模式中,所有的设计模式都遵循这一原则;

【开闭原则
(OCP)】

开闭原则的核心思想就是:一个对象对扩展开放,对修改关闭。它的英文缩写是
OCP,英文全称是 Open for Extension, Closed for Modification。

其实开闭原则的意思就是:对类的改动是通过增加代码进行的,而不是改动现有的代码。也就是说,软件开发人员一旦写出了可以运行的代码,就不应该去改变它,而是要保证它能一直运行下去,如何才能做到这一点呢?这就需要借助于抽象和多态,即把可能变化的内容抽象出来,从而使抽象的部分是相对稳定的,而具体的实现层则是可以改变和扩展的;

【里氏替换原则
(LSP)】

里氏替换原则的核心思想就是:在任何父类出现的地方都可以用它的子类来替代。它的英文缩写是
LSP,英文全称是 Liskov Substitution Principle。

其实里氏替换原则的意思就是:同一个继承体系中的对象应该有共同的行为特征。里氏替换原则关注的是怎样良好地使用继承,也就是说不要滥用继承,它是继承复用的基石;

【依赖注入原则
(DIP)】

依赖注入原则的核心思想就是:要依赖于抽象,不要依赖于具体的实现。它的英文缩写是
DIP,英文全称是 Dependence Inversion Principle。

其实依赖注入原则的意思就是:在应用程序中,所有的类如果使用或依赖于其他的类,则都应该依赖于这些其他类的抽象类,而不是这些其他类的具体实现类。抽象层次应该不依赖于具体的实现细节,这样才能保证系统的可复用性和可维护性。为了实现这一原则,就要求开发人员在编程时要针对接口编程,而不针对实现编程;

【接口分离原则
(ISP)】

接口分离原则的核心思想就是:不应该强迫客户程序依赖它们不需要使用的方法。它的英文缩写是
ISP,英文全称是 Interface Segregation Principle。

其实接口分离原则的意思就是:一个接口不需要提供太多的行为,一个接口应该只提供一种对外的功能,不应该把所有的操作都封装到一个接口当中;

【迪米特原则
(LOD)】

迪米特原则的核心思想就是:一个对象应当对其他对象尽可能少的了解。它的英文缩写是
LOD,英文全称是 Law of Demeter。

其实迪米特原则的意思就是:降低各个对象之间的耦合,提高系统的可维护性。在模块之间,应该只通过接口来通信,而不理会模块的内部工作原理,它可以使各个模块耦合程度降到最低,促进软件的复用;

【优先使用组合而不是继承原则
(CARP)】

优先使用组合而不是继承原则的核心思想就是:优先使用组合,而不是继承。它的英文缩写是
CARP,英文全称是 Composite / Aggregate Reuse Principle。

其实优先使用组合而不是继承原则的意思就是:在复用对象的时候,要优先考虑使用组合,而不是继承,这是因为在使用继承时,父类的任何改变都可能影响子类的行为,而在使用组合时,是通过获得对其他对象的引用而在运行时刻动态定义的,有助于保持每个类的单一职责原则;

发表评论

电子邮件地址不会被公开。 必填项已用*标注