设计模式

2023-05-16

1.什么是模式

设计模式是对某些典型易变问题的特定解决方案,这些问题和解决方案经过分类总结,并且为了方便交流给每个解决方案都起了特定的名字。

模式是为了解决变化的问题,将变化的问题进行封装,让变化单独变化而互不影响,以达到项目系统的扩展性,模式是一种更高层次的代码复用。

设想一下,假如我们的项目是一次性项目,一旦发布,以后再也不会修改,那么我们完全没有必要使用设计模式,反而使用了设计模式会增加系统的复杂度,但是一次性的项目在我们实际工作环境中是极少的,如果实际工作环境中都是一次项目,那个时候我们开发人员都应该转行了。

2.为什么要使用模式

  • 每一个设计模式都是为了解决一类特定的问题而存在,是一种更高层级的复用

  • 每一个设计模式都有一个特定的名字,团队协作中,既可以减少沟通成本,同时也可以保证项目质量

  • 模式有助于开发人员提高解决问题的思考层次,能够让我们的系统更容易扩展

3.六大原则

前辈们经过长期经验积累总结了许多设计模式,如单例模式、工厂模式、享元模式、策略模式、观察者模式等等,这些模式都是为了解决某一类特定的问题而设计,但是我们在日常编码过程中可以把这些模式作为一种参考即可,我们在编码过程中满足模式的原则即认为是好的代码。

模式有以下六大原则

名称解释
单一职责要存在多于一个导致类变更的原因,通俗的说,即一个类只负责一项职责
里式代换子类可以扩展父类的功能,但不能改变父类原有的功能
依赖倒置高层模块不应该依赖低层模块,二者都应该依赖其抽象,抽象不应该依赖细节,细节应该依赖抽象
接口隔离客户端不应该依赖它不需要的接口,一个类对另一个类的依赖应该建立在最小的接口上
迪米特法则一个对象应该对其他对象保持最少的了解
开闭原则一个软件实体如类、模块和函数应该对扩展开放,对修改关闭

3.1 单一职责

单一职责看似简单,其实并不简单,这个原则告诉了我们划分一个类功能的标准,下面我们看一段常见的代码片段

<?php
/**
 * Class Login
 * @datetime 2020/7/12 4:13 PM
 * @author roach
 * @email jhq0113@163.com
 */
abstract class Login
{
    /**微信appId
     * @var string
     * @datetime 2020/7/12 4:11 PM
     * @author roach
     * @email jhq0113@163.com 
     */
    public $wxAppid;

    /**微信密钥
     * @var string
     * @datetime 2020/7/12 4:11 PM
     * @author roach
     * @email jhq0113@163.com 
     */
    public $wxAppSecret;

    /**微信登录
     * @return mixed
     * @datetime 2020/7/12 4:12 PM
     * @author roach
     * @email jhq0113@163.com
     */
    abstract public function wxLogin();

    /**手机号验证码登录
     * @return mixed
     * @datetime 2020/7/12 4:12 PM
     * @author roach
     * @email jhq0113@163.com
     */
    abstract public function phoneLogin();
}

以上例程代码是一个用户登录的代码片段,为了演示方便作者把wxLoginphoneLogin两个方法实现细节进行了屏蔽。

以上代码有什么问题吗?不都是这么做登录的吗?

从模式原则来分析,假如微信登录实现细节发生修改,我们要修改Login类,手机号验证码登录实现细节发生修改,我们同样要修改Login类,如果我们要增加一种QQ授权登录方式,同样我们要修改Login

在模式的理论中,只要发生修改就会有风险,那么我们怎么解决这个问题呢?按照单一职责原则,作者把代码做了如下拆分优化

<?php
/**
 * Class Login
 * @datetime 2020/7/12 4:13 PM
 * @author roach
 * @email jhq0113@163.com
 */
abstract class WxLogin
{
    /**微信appId
     * @var string
     * @datetime 2020/7/12 4:11 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public $wxAppid;

    /**微信密钥
     * @var string
     * @datetime 2020/7/12 4:11 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public $wxAppSecret;

    /**
     * @param array $params
     * @return mixed
     * @datetime 2020/7/12 4:20 PM
     * @author roach
     * @email jhq0113@163.com
     */
    abstract public function login($params = []);
}

/**
 * Class PhoneLogin
 * @datetime 2020/7/12 4:20 PM
 * @author roach
 * @email jhq0113@163.com
 */
abstract class PhoneLogin
{
    /**
     * @param array $params
     * @return mixed
     * @datetime 2020/7/12 4:20 PM
     * @author roach
     * @email jhq0113@163.com
     */
    abstract public function login($params = []);
}

作者将Login类一分为二,微信登录的appId等依赖也放到各自类中,这样调用端如果想实现登录功能选择调用类就可以了。

现在我们分析,假如微信登录实现要发生修改,我们直接修改WxLogin类就可以了,同样的,假如手机号验证码登录实现要发生修改,我们只需要修改PhoneLogin类就可以了;假如我们要增加一种QQ授权登录的方式,此时我们增加一个QqLogin类就可以了。

3.2 里式代换

里式代换原则强调,我们能够扩展基类的原有功能,但不能改变基类原有的功能,这样很容易出现问题,下面还是看一段示例代码

<?php
/**
 * Class Model
 * @datetime 2020/7/12 4:30 PM
 * @author roach
 * @email jhq0113@163.com
 */
class Model 
{
    /**
     * @var \PDO
     * @datetime 2020/7/12 4:29 PM
     * @author roach
     * @email jhq0113@163.com 
     */
    private $_db;
    
    /**
    * @param \PDO $db
    * @datetime 2020/7/12 4:45 PM
    * @author roach
    * @email jhq0113@163.com
     */
    public function setDb(\PDO $db)
    {
        $this->_db = $db;
    }

    /**
     * @return \PDO
     * @datetime 2020/7/12 4:32 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public function getDb()
    {
        if(is_null($this->_db)) {
            $this->setDb(new \PDO('mysql:host=127.0.0.1;port=3306;dbname=roach', 'roach', 'roach'));
        }
        return $this->_db;
    }

    /**
     * @param null $name
     * @return string
     * @datetime 2020/7/12 4:36 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public function lastInsertId($name = null)
    {
        return $this->_db->lastInsertId($name);
    }
}

/**
 * Class UserModel
 * @datetime 2020/7/12 4:35 PM
 * @author roach
 * @email jhq0113@163.com
 */
class UserModel extends Model
{
    /**
     * @return PDO|string
     * @datetime 2020/7/12 4:35 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public function getDb()
    {
        return MyModel::db();
    }
}

/**
 * Class MyModel
 * @datetime 2020/7/12 4:34 PM
 * @author roach
 * @email jhq0113@163.com
 */
class MyModel
{
    /**
     * @var \PDO
     * @datetime 2020/7/12 4:33 PM
     * @author roach
     * @email jhq0113@163.com
     */
    private static $_db;

    /**
     * @return PDO
     * @datetime 2020/7/12 4:34 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public static function db()
    {
        if(is_null(self::$_db)) {
            self::$_db = new \PDO('mysql:host=127.0.0.1;port=3306;dbname=roach', 'roach', 'roach');
        }
        return self::$_db;
    }
}

以上代码也是真实遇到的,分析以上代码,在多人并行开发的团队,一个开发发现Model类的db并非真正的单例,于是这个开发在不改变业务调用代码的基础上,增加了MyModel类实现了约定单例,但是在实现UserModelgetDb方法的时候,改变了基类Model原有的getDb方法给_db私有属性赋值,这样导致基类lastInsertId方法不能使用,此时如果我们使用基类可以正常调用lastInsertId方法,但是使用子类UserModel调用lastInsertId就会发生异常。

以上代码违背了里式代换原则,那么我们应该如何修改呢?作者对UserModel进行了如下优化

<?php
/**
 * Class UserModel
 * @datetime 2020/7/12 4:35 PM
 * @author roach
 * @email jhq0113@163.com
 */
class UserModel extends Model
{
    /**
     * @return \PDO
     * @datetime 2020/7/12 4:35 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public function getDb()
    {
        $this->setDb(MyModel::db());
        return parent::getDb();
    }
}

这样修改后保证了单例,和里式代换

3.3 依赖倒置

依赖倒置的思想是个很经典扩展性思维,开发人员如果能够深刻理解依赖倒置原则,将有很大层次的提升,还是看如下代码

<?php
/**
 * Class FileLog
 * @datetime 2020/7/12 6:09 PM
 * @author roach
 * @email jhq0113@163.com
 */
class FileLog
{
    /**
     * @var string
     * @datetime 2020/7/12 6:05 PM
     * @author roach
     * @email jhq0113@163.com
     */
    protected $_logFile;

    /**
     * @param $fileName
     * @datetime 2020/7/12 6:06 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public function setLogFile($fileName)
    {
        $this->_logFile = $fileName;
    }

    /**
     * @param string $message
     * @param array  $context
     * @param string $leftPlace
     * @param string $rightPlace
     * @return string
     * @datetime 2019/8/30 18:00
     * @author roach
     * @email jhq0113@163.com
     */
    static public function interpolate($message, array $context = [], $leftPlace='{', $rightPlace='}')
    {
        if(empty($context)) {
            return $message;
        }

        $replace = [];
        foreach ($context as $key => $val) {
            $replace[$leftPlace . $key . $rightPlace] = $val;
        }
        return strtr($message, $replace);
    }

    /**
     * @param string $level
     * @param string $message
     * @param array  $params
     * @datetime 2020/7/12 6:07 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public function log($level, $message, $params = [])
    {
        $message = self::interpolate($message, $params);
        $message = json_encode([
            'datetime' => date('Y-m-d H:i:s'),
            'level'    => $level,
            'msg'      => $message,
            'clientIp' => $_SERVER['REMOTE_ADDR'],
            'url'      => $_SERVER['REQUEST_URI'],
            'method'   => $_SERVER['REQUEST_METHOD'],
            'host'     => $_SERVER['HTTP_HOST']
        ], JSON_UNESCAPED_UNICODE);
        file_put_contents($this->_logFile, $message.PHP_EOL, FILE_APPEND | LOCK_EX);
    }
}

/**
 * Class DbLog
 * @datetime 2020/7/12 6:11 PM
 * @author roach
 * @email jhq0113@163.com
 */
class DbLog
{
    /**
     * @var \PDO
     * @datetime 2020/7/12 6:11 PM
     * @author roach
     * @email jhq0113@163.com 
     */
    protected $_pdo;

    /**
     * @param \PDO $db
     * @datetime 2020/7/12 6:12 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public function setDb(\PDO $db)
    {
        $this->_pdo = $db;
    }

    /**
     * @param string $message
     * @param array  $context
     * @param string $leftPlace
     * @param string $rightPlace
     * @return string
     * @datetime 2019/8/30 18:00
     * @author roach
     * @email jhq0113@163.com
     */
    static public function interpolate($message, array $context = [], $leftPlace='{', $rightPlace='}')
    {
        if(empty($context)) {
            return $message;
        }

        $replace = [];
        foreach ($context as $key => $val) {
            $replace[$leftPlace . $key . $rightPlace] = $val;
        }
        return strtr($message, $replace);
    }

    /**
     * @param string $level
     * @param string $message
     * @param array  $params
     * @return int
     * @datetime 2020/7/12 6:16 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public function log($level, $message, $params = [])
    {
        $message = self::interpolate($message, $params);
        
        $stmt = $this->_pdo->prepare('INSERT INTO `logs`(`datetime`, `level`, `msg`, `clientIp`, `url`, `method`, `host`)VALUES(?,?,?,?,?,?,?)');
        
        $stmt->execute([
            date('Y-m-d H:i:s'),
            $level,
            $message,
            $_SERVER['REMOTE_ADDR'],
            $_SERVER['REQUEST_URI'],
            $_SERVER['REQUEST_METHOD'],
            $_SERVER['HTTP_HOST']
        ]);
        
        return $stmt->rowCount();
    }
}

/**
 * Class LogFactory
 * @datetime 2020/7/12 6:21 PM
 * @author roach
 * @email jhq0113@163.com
 */
class Logger
{
    /**
    * @param string $loggerName
    * @param string $level
    * @param string $message
    * @param array  $params
    * @return int|void
    * @datetime 2020/7/12 6:25 PM
    * @author roach
    * @email jhq0113@163.com
    */
    public function log($loggerName, $level, $message, $params = [])
    {
        if($loggerName === 'db') {
           $logger = new DbLog();
           $logger->setDb(new \PDO('mysql:host=127.0.0.1;port=3306;dbname=roach', 'roach', 'roach'));
           return $logger->log($level, $message, $params);
        }
                
        $logger = new FileLog();
        $logger->setLogFile('/tmp/logs/'.date('Y').'/'.date('m-d').'.log');
        return $logger->log($level, $message, $params);
    }
}

以上代码示例中,Logger类的log方法通过传入参数$loggerName来决定用哪个类去记录日志,这样Logger类直接依赖FileLogDbLog类,当我们再增加一个KafkaLog类打日志时,Logger同样需要依赖KafkaLog类,这样Logger类就需要发生修改,在模式领域,这样的代码认为不可以扩展,那么怎么优化呢?

<?php
/**
 * Class ILog
 * @datetime 2020/7/12 6:32 PM
 * @author roach
 * @email jhq0113@163.com
 */
abstract class ILog
{
    /**
     * @param string $message
     * @param array  $context
     * @param string $leftPlace
     * @param string $rightPlace
     * @return string
     * @datetime 2019/8/30 18:00
     * @author roach
     * @email jhq0113@163.com
     */
    static public function interpolate($message, array $context = [], $leftPlace='{', $rightPlace='}')
    {
        if(empty($context)) {
            return $message;
        }

        $replace = [];
        foreach ($context as $key => $val) {
            $replace[$leftPlace . $key . $rightPlace] = $val;
        }
        return strtr($message, $replace);
    }
    
    /**
     * @param string $level
     * @param string $message
     * @param array  $params
     * @return mixed
     * @datetime 2020/7/12 6:30 PM
     * @author roach
     * @email jhq0113@163.com
     */
    abstract public function log($level, $message, $params = []);
}

/**
 * Class FileLog
 * @datetime 2020/7/12 6:09 PM
 * @author roach
 * @email jhq0113@163.com
 */
class FileLog extends ILog
{
    /**
     * @var string
     * @datetime 2020/7/12 6:05 PM
     * @author roach
     * @email jhq0113@163.com
     */
    protected $_logFile;

    /**
     * @param $fileName
     * @datetime 2020/7/12 6:06 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public function setLogFile($fileName)
    {
        $this->_logFile = $fileName;
    }

    /**
     * @param string $level
     * @param string $message
     * @param array  $params
     * @datetime 2020/7/12 6:07 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public function log($level, $message, $params = [])
    {
        $message = self::interpolate($message, $params);
        $message = json_encode([
            'datetime' => date('Y-m-d H:i:s'),
            'level'    => $level,
            'msg'      => $message,
            'clientIp' => $_SERVER['REMOTE_ADDR'],
            'url'      => $_SERVER['REQUEST_URI'],
            'method'   => $_SERVER['REQUEST_METHOD'],
            'host'     => $_SERVER['HTTP_HOST']
        ], JSON_UNESCAPED_UNICODE);
        file_put_contents($this->_logFile, $message.PHP_EOL, FILE_APPEND | LOCK_EX);
    }
}

/**
 * Class DbLog
 * @datetime 2020/7/12 6:11 PM
 * @author roach
 * @email jhq0113@163.com
 */
class DbLog extends ILog
{
    /**
     * @var \PDO
     * @datetime 2020/7/12 6:11 PM
     * @author roach
     * @email jhq0113@163.com 
     */
    protected $_pdo;

    /**
     * @param \PDO $db
     * @datetime 2020/7/12 6:12 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public function setDb(\PDO $db)
    {
        $this->_pdo = $db;
    }

    /**
     * @param string $level
     * @param string $message
     * @param array  $params
     * @return int
     * @datetime 2020/7/12 6:16 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public function log($level, $message, $params = [])
    {
        $message = self::interpolate($message, $params);
        
        $stmt = $this->_pdo->prepare('INSERT INTO `logs`(`datetime`, `level`, `msg`, `clientIp`, `url`, `method`, `host`)VALUES(?,?,?,?,?,?,?)');
        
        $stmt->execute([
            date('Y-m-d H:i:s'),
            $level,
            $message,
            $_SERVER['REMOTE_ADDR'],
            $_SERVER['REQUEST_URI'],
            $_SERVER['REQUEST_METHOD'],
            $_SERVER['HTTP_HOST']
        ]);
        
        return $stmt->rowCount();
    }
}

/**
 * Class LogFactory
 * @datetime 2020/7/12 6:21 PM
 * @author roach
 * @email jhq0113@163.com
 */
class Logger
{
    /**
     * @param ILog   $logger
     * @param string $level
     * @param string $message
     * @param array  $params
     * @return mixed
     * @datetime 2020/7/12 6:32 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public function log(ILog $logger, $level, $message, $params = [])
    {
        return $logger->log($level, $message, $params);
    }
}

我们可以看到,在优化过程中,我们增加了ILog抽象类,并增加了抽象方法logLogger类中log方法不再依赖$loggerName,改为依赖抽象类ILog,这样,当我们增加KafkaLog类打日志时,Logger类不需要再做任何修改。

这就是典型的依赖倒置思想。

3.4 接口隔离

我们刚刚学会了依赖倒置,高层模块不应该依赖于低层模块儿,二者应该依赖其抽象。接口隔离告诉我们,这个抽象要有个度,应该建立在最小接口上。

看如下代码

<?php
/**
 * Interface IOrderAndLogin
 * @datetime 2020/7/12 6:45 PM
 * @author roach
 * @email jhq0113@163.com
 */
interface IOrderAndLogin
{
    /**
     * @param array $params
     * @return mixed
     * @datetime 2020/7/12 6:44 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public function order($params = []);

    /**
     * @param array $params
     * @return mixed
     * @datetime 2020/7/12 6:46 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public function login($params = []);
}

/**
 * Class ProductOrder
 * @datetime 2020/7/12 6:48 PM
 * @author roach
 * @email jhq0113@163.com
 */
class ProductOrder implements IOrderAndLogin
{
    /**
     * Order constructor.
     * @param array $params
     */
    public function order($params = [])
    {
        //各种下单操作
        return true;
    }

    /**
     * @param array $params
     * @return bool|mixed
     * @datetime 2020/7/12 6:48 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public function login($params = [])
    {
        return false;
    }
}

/**
 * Class WxLogin
 * @datetime 2020/7/12 6:50 PM
 * @author roach
 * @email jhq0113@163.com
 */
class WxLogin implements IOrderAndLogin
{
    /**
     * @param array $params
     * @return bool|mixed
     * @datetime 2020/7/12 6:49 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public function order($params = [])
    {
        return false;
    }

    /**
     * @param array $params
     * @return array|mixed
     * @datetime 2020/7/12 6:50 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public function login($params = [])
    {
        // 各种操作
        return [
            'userId' => time(),
            'nickname' => uniqid()
        ];
    }
}

/**
 * Class OrderController
 * @datetime 2020/7/12 6:55 PM
 * @author roach
 * @email jhq0113@163.com
 */
class OrderController
{
    /**
     * @param IOrderAndLogin $order
     * @return mixed
     * @datetime 2020/7/12 6:55 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public function orderAction(IOrderAndLogin $order)
    {
        return $order->order([
            'time' => time(),
        ]);
    }
}

以上代码我们可以看到,OrderController通过接口IOrderAndLogin依赖ProductOrder,但是IOrderAndLogin接口还有一个OrderController用不到的接口方法login,同时ProductOrder类根本不需要login方法,同样的,WxLogin类也不需要order方法,但是他们各自都必须去实现不需要的方法,这就不符合接口的单一职责原则。

这种优化思路很简单,把IOrderAndLogin接口拆分成IOrderILogin两个接口,ProductOrder类实现IOrder接口,WxLogin类实现ILogin接口,OrderController类依赖IOrder接口即可,由于代码很简单,不在提供实现细节。

3.5 迪米特法则

迪米特法则简单来说就是高内聚、低耦合,一个对象应该对其他对象保持最少的了解。

从耦合度来讲,继承>依赖,所以当我们使用继承的时候,除非是有明确的父子关系,否则不要乱用继承。

下面举一个简单的高内聚、低耦合的例子

<?php
/**
 * Created by PhpStorm.
 * User: Jiang Haiqiang
 * Date: 2020/7/12
 * Time: 4:09 PM
 */
class RedisCache
{
    /**
     * @var string
     * @datetime 2020/7/12 7:10 PM
     * @author roach
     * @email jhq0113@163.com 
     */
    public $host;

    /**
     * @var int
     * @datetime 2020/7/12 7:10 PM
     * @author roach
     * @email jhq0113@163.com 
     */
    public $port;

    /**
     * @var string
     * @datetime 2020/7/12 7:10 PM
     * @author roach
     * @email jhq0113@163.com 
     */
    public $password;

    /**
     * @var int
     * @datetime 2020/7/12 7:10 PM
     * @author roach
     * @email jhq0113@163.com 
     */
    public $db;

    /**
     * @var \Redis
     * @datetime 2020/7/12 7:11 PM
     * @author roach
     * @email jhq0113@163.com 
     */
    private $_instance;

    /**
     * @return \Redis
     * @datetime 2020/7/12 7:13 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public function getRedis()
    {
        if(is_null($this->_instance)) {
            $redis = new \Redis();
            $redis->connect($this->host, $this->port);
            $redis->auth($this->password);
            $redis->select($this->db);
            $this->_instance = $redis;
        }
        return $this->_instance;
    }

    /**
     * @param string $key
     * @return bool|string
     * @datetime 2020/7/12 7:14 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public function get($key)
    {
        return $this->getRedis()->get($key);
    }

    /**
     * @param string $key
     * @param string $value
     * @param int $timeout
     * @return bool
     * @datetime 2020/7/12 7:14 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public function set($key, $value, $timeout = 60)
    {
        return $this->getRedis()->set($key, $value, $timeout);
    }
}

以上RedisCache封装类,也许大家在自己的项目中肯定看到过类似的代码,大家会觉得这有什么问题吗?

其实从高内聚低耦合的角度来讲,host,port,passworddb属性以及getRedis方法,对于其他类来说根本不需要知道,本着高内聚低耦合的思想,这些的访问权限至少要控制在protected级别,这样从技术角度避免了泄露和被修改的可能性。

3.6 开闭原则

开闭原则简单来说就是对修改关闭,对扩展开放,此原则理解起来稍复杂,我们可以狭义的这样理解。

如果我们的系统软件已经上线运行了,当发生新需求变化时,我们是通过增加代码实现来满足新需求而不是修改原来的代码来实现新需求,当然这是一个狭义的理解。

我们回头看依赖倒置的例子就是一个符合开闭原则的典型案例,当我们增加KafkaLog需求时,我们是通过增加KafkaLog类实现来满足需求,原有的FileLog类和DbLog类没有发生任何更改,这样就是一个典型的对扩展开放,对修改关闭的一个例子。

好了,设计模式的六大原则我们都通过案例学习完了,你都学会了吗?

学习更多内容: https://404.360tryst.com

我的视频课程: https://edu.csdn.net/course/detail/9933

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

设计模式 的相关文章

  • 常用设计模式总结

    设计模式的相关知识 很多书籍和博客中都有详细总结 本文总结的目的 1 将自己学习到的设计模式的知识按照自己的逻辑重新总结 方便查看和记忆 2 方便让自己对设计模式中常用的知识有一个系统的认知 设计模式 话设计模式 书中提到 24 种设计模式
  • 设计模式三: 代理模式(Proxy) -- JDK的实现方式

    简介 代理模式属于行为型模式的一种 控制对其他对象的访问 起到中介作用 代理模式核心角色 真实角色 代理角色 按实现方式不同分为静态代理和动态代理两种 意图 控制对其它对象的访问 类图 实现 JDK自带了Proxy的实现 下面我们先使用JD
  • 设计模式之(三)---工厂方法模式

    女娲补天的故事大家都听过吧 这个故事是说 女娲在补了天后 下到凡间一看 哇塞 风景太优美了 天空是湛 蓝的 水是清澈的 空气是清新的 太美丽了 然后就待时间长了就有点寂寞了 没有动物 这些看的到 都是静态的东西呀 怎么办 别忘了是神仙呀 没
  • C++ 装饰器模式

    什么是装饰器模式 装饰器模式是一种结构型设计模式 实现了在不改变现有对象结构的的同时又拓展了新的功能 装饰器本质上是对现有对象的重新包装 同时装饰器又称为封装器 如何理解装饰器模式 以笔记本电脑为例 当我们购买了一台新笔记本电脑 但我们发现
  • Java设计模式:装饰者模式(Decorator Pattern)

    装饰者模式 涉及的重要设计原则 类应该对扩展开放 对修改关闭 装饰者模式定义 装饰者模式动态地将责任附加到对象上 若要扩展功能 装饰者提供了比继承更有弹性的替代方案 UML类图 装饰者模式事例 咖啡店 咖啡种类 1 深焙咖啡 DarkRoa
  • java需会(转载)

    一 基础篇 1 1 Java基础 面向对象的特征 继承 封装和多态 final finally finalize 的区别 Exception Error 运行时异常与一般异常有何异同 请写出5种常见到的runtime exception i
  • 设计模式——简单工厂模式

    简单工厂模式定义为 简单工厂模式又称为静态工厂方法模型 它属于类创建型模式 在简单工厂模式中 可以根据参数的不同返回不同类的实例 简单工厂专门定义一个类来负责创建其他类的实例 被创建的实例通常都具有共同的父类 简单工厂模式结构图 简单工厂模
  • 设计模式——导论

    作为软件开发人员 我们在平时工作的过程中 往往需要编写很多的代码来实现我们的需求 很多的时候会造成代码臃肿和代码冗余的情况 这个时候我们需要引入一个理念 设计模式 设计模式存在的意义在于 1 使得我们的代码更加精炼 2 使我们代码的可读性更
  • 分享几个项目中用到的设计模式

    前言 之前项目中出于扩展性和有雅性的考虑 使用了多种设计模式进行项目框架的设计 主要的一些设计模式是单例模式 工厂模式 策略模式 责任链模式 代理模式这几种 现在依次讲讲这几个的主要是实现方式和在我们项目中的应用场景 核心设计模式分享 单例
  • 设计模式之享元模式

    享元模式 就是共享技术 对于系统中存在大量相同的对象 把他们抽取成一个对象放在缓存中进行使用 这样可以大大节省系统资源 例如 围棋棋盘上有两种棋子 一个是黑子 一个是白子 如果在下棋的时候每下一个棋子就要new一个棋子对象 那么就会有大量的
  • java-IO流(5)-IO流中的设计模式(装饰器模式和适配器模式)的介绍

    目录 1装饰器模式 1 1定义 1 2代码实现 1 3装饰器特点 1 4装饰器在IO流中的使用 2配适器模式 2 1Adapter适配器 2 2代码实例 2 3适配器特点 2 4适配器优缺点 2 5适配器在IO中的使用 3装饰器与适配器异同
  • [C++]外观模式

    外观模式 Facade Pattern 隐藏系统的复杂性 并向客户端提供了一个客户端可以访问系统的接口 这种类型的设计模式属于结构型模式 它向现有的系统添加一个接口 来隐藏系统的复杂性 这种模式涉及到一个单一的类 该类提供了客户端请求的简化
  • 程序员必知的23种设计模式之组合模式

    文章目录 1 模式引出 学校院系展示需求 1 1 传统方案 1 2 传统方案问题分析 2 组合模式基本介绍 2 1 方案修改 3 组合模式解决的问题 4 组合模式的注意事项和细节 1 模式引出 学校院系展示需求 编写程序展示一个学校院系结构
  • 设计模式——State(状态)模式

    目录 前言 1 定义 2 适用性 3 结构 3 1 结构图 3 2 参与者 4 应用举例 4 1 State TcpState 4 2 Context TcpConnection 4 3 ConcreteState ListeningTcp
  • DDD专家张逸:构建领域驱动设计知识体系

    张逸 读完需要 5分钟 速读仅需 2 分钟 领域驱动设计专家 曾就职于 ThoughtWorks 作为 Lead Consultant 为客户提供架构设计 大数据分析 持续交付 代码质量 敏捷管理等咨询服务 著译作包括 软件设计精要与模式
  • 哈工大2020软件构造Lab3实验报告

    本项目于4 21日实验课验收 更新完成 如果有所参考 请点点关注 点点赞GitHub Follow一下谢谢 2020春计算机学院 软件构造 课程Lab3实验报告 Software Construction 2020 Spring Lab 3
  • 谁能想到Java多线程设计模式竟然能被图解,大佬就是大佬,太牛了

    设计模式 Design pattern 代表了最佳的实践 通常被有经验的面向对象的软件开发人员所采用 设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案 这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的
  • 设计模式(3)--对象结构(5)--外观

    1 意图 为子系统中的一组接口提供一个一致的界面 Facade模式定义了一个高层接口 这个接口使得 这一子系统更加容易使用 2 两种角色 子系统 Subsystem 外观 Facade 3 优点 3 1 对客户屏蔽了子系统组件 减少了客户处
  • C++设计模式 #3策略模式(Strategy Method)

    动机 在软件构建过程中 某些对象使用的的算法可能多种多样 经常改变 如果将这些算法都写在类中 会使得类变得异常复杂 而且有时候支持不频繁使用的算法也是性能负担 如何在运行时根据需求透明地更改对象的算法 将算法和对象本身解耦 从而避免上述问题
  • 系列一、 单例设计模式

    一 单例设计模式 1 1 概述 单例模式 Singleton Pattern 是Java中最简单的设计模式之一 这种类型的设计模式属于创建者模式 它提供了一种创建对象的最佳方式 这种模式涉及到一个单一的类 该类负责创建自己的对象 同时确保只

随机推荐

  • 前端图片裁剪cropper.js

    背景 xff1a 我们在项目中常常都会有更换登陆人头像的需求 xff0c 这个很简单直接上传一张图片展示就可以了 xff0c 但是如果上传的图片需要剪裁成正方形的话就需要我们来做一些特殊的处理 xff1b 这里我们会用到一个图片剪裁插件 x
  • 新产品开发之C流程 (C-flow)

    关于新产品开发的C流程 C flow xff0c 是世界上大公司采用的标准开发流程 xff0c 十分基本也十分重要 xff0c 但是网上关于C流程介绍相关的资料很少 xff0c 所以花点时间整理一下相关的资料 下面以软件BSP开发为例 xf
  • Arduino的Stepper库函数及其控制步进电机(ULN2003)调速

    问题来源 最近自学Arduino xff0c 在使用步进电机时开始没能使步进电机转起来 xff0c 转起来后感觉没法调速 xff0c 遂完成此篇笔记供自己后续查阅以及方便遇到相同问题的诸君寻找灵感 对于如何使步进电机转动就不详述 xff0c
  • 一、电脑端实现单片机与ESP8266的通信

    1 准备工具 xff08 硬件 软件 xff09 硬件 xff1a 51单片机开发板 ESP8266无线模块 xff08 ESP8266 01 xff09 TTL USB串口 杜邦线 数据线 xff1b 软件 xff1a keil uv4单
  • 错误 LNK2038 检测到“_ITERATOR_DEBUG_LEVEL”的不匹配项: 值“2”不匹配值“0”(main.obj 中)

    前言 vs2019报错如下图 xff1a 错误 LNK2038 检测到 ITERATOR DEBUG LEVEL 的不匹配项 值 2 不匹配值 0 main obj 中 错误原因 1 产生这个问题的原因是当前工程是Debug版本 xff0c
  • 三种获取字节码对象的方式及区别

    方式一 xff1a 对象 getClass 方法是 根对象Object的方法 是其他类继承Object的getClass方法 方式二 xff1a 类名 class xff0c 你可以理解为字节码本身就是静态的 xff0c 类加载的时字节码就
  • 关于接口与Object 类的关系

    看到这个标题 xff0c 你或许就会想好自己的那份答案 但事实上这个确实没有答案 xff0c 至少没官方证明它们之间的基友关系 看法一 xff1a 因为老师说 xff0c 你可以把接口看作是特殊的类 xff0c 所以不假思索的就认为接口也
  • 单词博弈Java实现(借鉴“miss若尘”博客中写的解题思路)

    单词博弈Java实现 xff0c 已经通过庞果网的用例测试 代码如下 import java util HashMap public class WordGameFinalTest public static int who String
  • 我的2013,梦在路上

    我的2013 xff0c 在路上 今年最后一次给姐姐打电话 xff0c 她在那里像我炫耀自己和爸爸妈妈一起跨年 xff0c 说1314的意义 xff0c 而我还在北京苦逼着 回想2013年对于我来说 xff0c 或许是不错的一年 这一年我进
  • mysql安装时的粗心错误:last error unable to update security settings. access denied for user 'root' @ 'localh

    来自 梦想家haima 39 s blog gt http blog dreamwe cn 这个报错出现在mysql最后 当你看到mysql的最后一步需要设置密码可能你开心得很 Mysql就快安装好了 赶快输入三行密码都是root 结果报下
  • @SuppressWarnings

    简介 xff1a java lang SuppressWarnings是J2SE 5 0中标准的Annotation之一 可以标注在类 字段 方法 参数 构造方法 xff0c 以及局部变量上 作用 xff1a 告诉编译器忽略指定的警告 xf
  • 欢迎使用CSDN-markdown编辑器

    欢迎使用Markdown编辑器写博客 本Markdown编辑器使用StackEdit修改而来 xff0c 用它写博客 xff0c 将会带来全新的体验哦 xff1a Markdown和扩展Markdown简洁的语法代码块高亮图片链接和图片上传
  • Linux之强大的awk

    来自 梦想家 Haima s blog gt http blog dreamwe cn awk简介 awk是Linux中的一个命令 xff0c 用来做文本处理与分析 xff0c 功能简单强悍 xff0c 同时它也是一门编程语言 awk处理文
  • ucos源码分析(一)

    时间 2018 01 27 本人目前是大三学生 电子信息工程专业 xff0c 在大学前俩年的时间 xff0c 一直在使用和学习单片机 xff0c 不过也仅仅是从 xff15 xff11 到 xff13 xff12 xff0c 马上要面临就业
  • 随笔——Python & C++ 混用——使用cmake生成c++动态库

    Python amp C 43 43 混用 xff0c 使用cmake 生成c 43 43 动态库 xff0c 可供python调用 说明 xff1a test cpp为源文件 add h为头文件 源文件使用第三方库opencv CMake
  • 机会都是留给有准备的人,你在准备什么?| 每天成就更大成功

    最近养成一个习惯 xff0c 就是每晚抄书 xff0c 大概200字左右 xff0c 在抄书的过程中反思了一个问题 xff1a 因为字写的不漂亮 xff0c 于是就在抄书的时候就有意的去练字 xff0c 这样抄书的精力就被分散了一部分出去
  • 《SQL Server 2005开发技术大全》分享一本书

    数据库是一个非常重要的领域 xff0c 不管是什么样的系统 xff0c 都必须要与数据库打交道 xff0c 因此作为一个程序员来讲 xff0c 数据库的基本知识技能是必须要掌握的 我接触C 已经有两年半的时间了吧 xff0c 在学习之初是接
  • golang语言rsa加解密及签名验签

    golang语言rsa加解密及签名验签 96 rsa 96 算法概述 96 Rsa 96 结构体封装封装的优点使用案例 rsa算法 概述 rsa是一种非对称的可逆的加密算法 xff0c 对加密数据长度有限制 xff0c 同时rsa也提供了数
  • 异常与错误处理

    异常与错误处理 PHP的异常与错误是分开的 xff0c 当程序出现异常时会throw一个 Exception 或子类 对象 xff0c 但是当出现错误时会触发一个错误 1 异常处理 1 1 通过try catch主动处理异常 span cl
  • 设计模式

    1 什么是模式 设计模式是对某些典型易变问题的特定解决方案 xff0c 这些问题和解决方案经过分类总结 xff0c 并且为了方便交流给每个解决方案都起了特定的名字 模式是为了解决变化的问题 xff0c 将变化的问题进行封装 xff0c 让变