设计模式

架构技巧系列之 — 单一职责原则

今天,怀着无比兴奋的心情来和大家扯扯平时同学经常会问我的问题: 1、如何从头开始写一个大型软件? 2、如何成为一名架构师? 3、面向对象开发思想究竟有什么用? 4、我感觉学完C/C++后为什么还是不会写大型程序? 5、设计模式究竟是什么?      其实,大型软件开发过程并不像想象中的那么神秘和困难,虽然从规模上看,大型软件开发确实够大,但是完全可以尽可能的使其过程简单化。      如何做到呢?经验!前辈们已经给我们总结了很多的开发经验,这些经验在今天已经浓缩成了一系列可以复用的开发准则,因此,站在巨人的肩膀上必然能使我们走得更远。所以,今天就先给大家介绍一个比较简单的设计准则:单一职责原则!       什么是单一职责原则? 英文名称为 Single Responsibility Principle ,简称 SRP ,即一个实体只做一件事,每个实体的功能是单一的。比如:一个函数的功能是单一的,一个类的功能是单一的。这个原则看起来是多么的容易啊,可是事实真的如此吗? 我们通过一个例子来说明问题。 下面是一个用户接口的设计: 这个设计看上去是那么的合情合理,虽然我的例子中的类是User但是把它换成 Student , Network 等都是可以的。所有与User相关的操作都集中在一个接口中,确实看上去挺好的,工作中也是这么做的。但是从设计的角度,这里却存在了一个非常严重的问题。因为这个接口的设计违背了 SRP 原则, User 的信息操作和 User 的行为操作被集中在了一起。 IUser接口负责了两件事: 1.Business Object Operation 2.Business Logic Operation      如果说User是需求上下文的中的一个实体,那么单纯的User信息我们应该抽象一个接口,即:Business Object Operation。而User之间的关系,User 与其它实体间的关系(如:Role,Department 等),以及User可以执行的操作等等应该抽象出另外的接口,即:Business Logic Operation。于是,有了下面的设计: 将职责分开后的代码示例:     上面将一个接口分开为两个接口的动作就是基于的 SRP 原则,那么究竟什么SRP原则呢?       SRP的本质:有且仅有一个原因会引发类的改变。 (There should never be more than one reason for a class to change.) 为了大家更进一步的理解这个原则,我们再来看一个例子。比如下面这个接口: 对于现代的手机来说,肯定有拨号,通话,挂机的功能,否则那也能叫手机?!因此,从实际的角度来说,这个接口设计应该算是完美的了。但,事实是这样吗?我们来分析一下,其实从功能上来说这个接口包含了两个职责:一个是通讯链路的管理,另一个是数据的传输。 对于实际情况: 1、当通讯协议不同时, dial 和 hangup 的方式不同 2、链路连通后,对于不同的数据,传输方式不同,如:文本,语音和图像的传输方式不同,但这些都可以是现代手机的聊天方式      那么这两个职责会相互影响吗?      链路在连接成功后即可传输数据,并且链路根本不会关心数据是以何种方式进行编码,也不会关心究竟传输的是文本,语音还是图像。对于链路而言,只负责传输二进制流。然而对于具体的数据传输而言,只要链路连通即可,不会关心是基于 TCP,  UDP 还是 CDMA,也不会关心这条链路是如何连通的。这样我们可以得出结论,这个两个职责是比较独立的,应该将接口重新设计。 通过上面的例子,我们可以得到下面的推论: 1、接口复杂性降低,所有的接口都是清晰的单一职责定义 2、更容易适应需求变更,每个变更只影响少数相互独立的接口 3、代码可读性和代码维护性提高      ...
阅读全文
架构师

LANMP架构:nginx反向代理的实现

 LAMP架构,即Linux Apache,Mysql,PHP,在企业级产品中,应用的非常广泛,因为它的开源免费,配置灵活,安全性好而备受各大中小型企业的喜爱。但近年来,nginx的出现,将慢慢打破被apche统治多年的WEB市场。nginx是俄罗斯人开发的,官网上介绍说它是一个轻量级,高可配,为了性能而生的新一代WEB服务器,特别是在静态页面的请求上资源消耗相当少。接下来我将以我们公司产品中遇到的实际情况做一个示例说明,因为讲这个概念和原理的人太多了,以下是我推荐的一些网址: 运维与架构:http://www.nginx.cn/ Apache官网:http://www.apache.org/ nginx官网:http://nginx.org/ PHP官网:http://php.net/ 在Centos中配置LAMP环境很容易,只需要执行以下几条命令即可。什么?你想用源码编译,那也可以,多多折腾总会有收获的。 yum install php mysql httpd -y 安装完成后,在httpd的配置文件,默认路径为/etc/http/conf/httpd.conf加入以下模块和对应index.php即可。并将Listen端口改为9000,后面配置反向代理有用。 LoadModule php5_module modules/libphp5.so DirectoryIndex index.php Listen  9000 至于更详细的配置这里不再多说,网上的教程多如牛毛,大家可以多多问度娘,谷歌。 那么LANMP,大家应该已经想到了,就是Linux+Apache+Nginx+Mysql+Php了,那么问题来了,为什么要用到这种架构呢? 这种架构需求来源于我们公司的一个具体的性能需求,公司之前采用的是LAMP架构,当用户并发数一多时,各种wget请求和前台页面请求并发连接很多,很快就把apache的连接给占用完了,但这个时候内存还明显有很多剩余,这时我查看了下apache的配置,发现只配置了MaxClientRequest数为200,也是因为考虑太大的话,全部是php业务请求时,内存会显得不足。其实这时面最大的一个问题就是一些静态页面的请求导致apache的连接被占用,并发上不去,那么这个时候我就想到了LANMP架构,利用nginx的反向代理功能,来处理连接的静态请求,至于PHP动态请求则交给apache的新连接端口去处理。我们以一个图来加深对反向代理原理的理解: 在我们这个模型中,代理服务器仅仅是把PHP需要处理的业务请求发送给了apache服务器,其它静态请求全部被代理服务器档了回去,其实说到这里大家应该都明白了,这就是一个负载均衡的实例了,不过我们的上游服务器只有一台,即应用服务器C,如果有多台应用服务器,那么就是大型企业,如淘宝等应用的集群了,那么代理服务器将通过一系列的计算方法将请求均匀发送给应用服务器,“均匀”的方法有很多,比如IP地址散列,随机值等。 下面是具体的实施方法: 首先需要下载好几个包,分别是pcre  nginx-1.4.4.tar.gz 为了方便以后扩展,我们把ssl模块也编译了进去,至于编译方法和包的获取大家可以去参考我分享的http://www.nginx.cn/: ./configure --prefix=/usr/local/nginx --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/tmp/logs/nginx/error.log --http-log-path=/tmp/logs/nginx/access.log --pid-path=/var/run/nginx/nginx.pid --lock-path=/var/lock/nginx.lock --user=apache --group=apache --with-http_ssl_module --with-http_flv_module --with-http_stub_status_module --with-http_gzip_static_module --http-proxy-temp-path=/tmp/ --http-fastcgi-temp-path=/tmp --with-pcre=/usr/local/pcre 接着执行: make make install 到此nginx已经安装好了,这个时候我们缺少一个启动脚本,我们来写一个,名字为nginx,内容如下: #! /bin/sh # chkconfig: 2345 55 25 # Description: Startup script for nginx webserver on Debian. Place in /etc/init.d and # run 'update-rc.d -f nginx defaults', or use the appropriate command on your # distro. For CentOS/Redhat run: 'chkconfig --add nginx' ### BEGIN INIT INFO # Provides: nginx #...
阅读全文

求两个自然数组中的交集

题目:有2个自然数组 里面的保存着100以内的自然数,编程求出两个数组的交集。数组中可能有重复的数字,输出的交集中不包含重复的数字。   我的实现代码如下:#include <stdio.h> #include <string.h> /*输出交集数组,同时返回交集元素个数*/ int NaturalUnion(int a, size_t len_a, int b, size_t len_b, int c) {         int tmp;         int i,index=0;         if(a && b && (len_a != 0) && (len_b != 0))         {                 memset(tmp,-1,sizeof(tmp)/sizeof(int));                 for(i=0;i<len_a;i++)                 {                         tmp = a;                 }                 for(i=0;i<len_b;i++)                 {                         if(b == tmp)                         {                                 c = tmp;                         }                 }         }         return index; } int main(int argc,char *argv) {         int a = {1,2,3,3,4,6};         int b = {3,4,5,7,8,9,10};         int c;         int count = 0,i;         count = NaturalUnion(a,sizeof(a)/sizeof(int),b,sizeof(b)/sizeof(int),c);         for(i=0;i<count;i++)         {                 printf("%d\n",c);         }         return 0; }  
阅读全文

回溯法

回溯法是一种比枚取发更好的算法,回溯法有一定的框架,这里先给出回溯的法的一般框架: //m表示数组有m个元素,n表示数组的最大元素值为n 输入正整数n, m(n>=m) i = 0; a = <元素初始值> while (1) { // i每次取得的值,和前面已得到的值进行比较,看是否能满足 for (g = 1, k = i - 1; k >= 0; k--) { if (<约束条件1>) g = 0; //检测约束条件,不满足返回 } if (g && <约束条件2>) { print(a) //输出一个解 } if (g && i < m) { i++; a = <元素初始值>; contiue; } while (a == <回溯点> && i > 0) { i--; //向前回溯 } // 达到退出循环的条件 if (i == 0 && a == <回溯点>) { break; } else { a++; } } 下面用回溯法来解决一个典型的回溯问题,八皇后问题,问题描述就省率了,直接上算法 #include <stdio.h> #include <stdlib.h> int main(int argc, const char *argv) { int...
阅读全文
算法

串的模式匹配算法

一.朴素模式匹配算法 int strsub(const char* par, const char* sub, int pos)    算法的基本思想:从主串par的第pos字符开始和子串sub的第一个字符比较,若相等,则继续捉个比较后续字符,否则则从主串的下一个字符起在重新和子串sub起始字符开始比较。以此类推,直至主串par中每个字符和子串sub的连续序列相等,则匹配相等,返回子串sub在主串par中的序号。否则匹配不成功,返回-1.下面是一个算法的实现 #include <stdio.h> #include <string.h> /* --------------------------------------------------------------------------*/ /** * @brief 从主串中的指定位置查找指定的子串是否存在 * * @param par 主串 * @param sub 子串 * @param pos 从主串中的指定位置 * * @returns 如果存在返回在主串中的位置,不存在返回-1 */ /* ----------------------------------------------------------------------------*/ int strsub(const char* par, const char* sub, int pos) { /* i为主串当前位置的指针,j为子串当前位置的指针 */ int i = pos, j = 0; int par_len = strlen(par); int sub_len = strlen(sub); int ret = -1; while (i < par_len && j < sub_len) { if (par == sub) { /* 当主串和子串当前字符相等时,指针前移 */ i++; j++; } else { /* 当有元素不相等时,则主串回退,子串从头开始 */ i...
阅读全文