Just One Night




1980年,Eric Clapton在日本东京Budokan Theatre举办了一场个人演出。《Just One Night》这张专辑记录当晚蓝调之夜的盛况。Eric Clapton在演出中演唱了很多经典的作品,无论是Blues味十足《Double Trouble》亦或是脍炙人口的《Wonderful Tonight》。个人很喜欢这个Live版的《Double Trouble》,Eric Clapton在70年代渐渐转向流行乐,可是这场演出可是货真价实的Night Of Blues。

Eric Clapton算是个人很喜欢的乐手之一。他1945年出生于英国小镇Ripley,从小就对Blues感兴趣的他15岁就开始学习吉他。1962年,年仅17岁的Eric Clapton从金斯顿艺术大学(Kingston College of Art)辍学,开始凭借自己的吉他演奏技术在英国当地的大小酒馆里演出养活自己。成名之后他回忆道,“当时只要能看到Bo Diddley与Chuck Berry一眼,我就高兴的昏了头。所以当我发现Muddy Walters与Robert Johnson时,对我的影响力都很大。”

早期的Eric Clapton玩的是比较传统的Blues或者Blues Rock。Eric Clapton的伟大不在于玩Blues玩得溜,而是在于他把Blues这种黑人音乐加入个人特色,进行扩展,使其大众化,也形成了个人风格。

如今71岁的Eric Clapton依然活跃在舞台上面,Eric Clapton除了坚持每隔几年出一张新唱片外,还每三年举办一次Crossroads演唱会,邀请朋友们一起上台Jam。




2013 Crossroads Guitar Festival(左下角为Eric Clapton)
这里放一个Eric Clapton他父亲的彩蛋


寻找父亲
虽然克莱普顿的祖父母将他身世的真相全部告诉他,然而他父亲的确切身份在很多年后还是一个谜。克莱普顿只知道他父亲叫做爱德华·弗赖尔,没有其它更多的信息。这是克莱普顿焦虑不安的源泉,在他1988年的作品“父亲的眼睛”(My Father’s Eyes)中他写道:

“我是如何来到世间?什么时候我所有的愿望可以实现?…当我看到我父亲的眼睛。”
(How did I get here? When will all my hopes arrive?…When I look in my father’s eyes.)

一个多伦多的叫麦克尔的旅行者,开始着手揭开谜团。他调查了加拿大军队的服役档案,并寻找所有名字为爱德华·弗赖尔的人,终于找到了真相。他父亲全名为爱德华·瓦特·弗赖尔,于1920年3月21日在蒙特利尔出生,于1985年5月15日在北约克逝世。弗赖尔是一个音乐家(钢琴和萨克斯),他终生流浪,几度结婚,并育有几个孩子。显而易见,他从来不知道自己是埃里克·克莱普顿的父亲。

WordPress 中文tab出现404的解决办法

打开wordpress的wp-includes目录下class-wp.php文件

1
2
3
4
5
6
$pathinfo = isset( $_SERVER['PATH_INFO'] ) ? $_SERVER['PATH_INFO'] : '';
修改为:
$pathinfo = isset( $_SERVER['PATH_INFO'] ) ? mb_convert_encoding($_SERVER['PATH_INFO'],'utf-8', 'GBK') : '';
list( $req_uri ) = explode( '?', $_SERVER['REQUEST_URI'] );
修改为:
list( $req_uri ) = explode( '?', mb_convert_encoding($_SERVER['REQUEST_URI'], 'utf-8','GBK') );


其实WP主要原因是WP存储在DB中的数据编码是UTF-8,而Windows系统默认的编码是GBK,才会导致问题的出现。
修改之后,中文连接在WP后台显示不正常,因为WP后台默认UTF-8编码。



Linux下使用vsftpd搭建ftp

查看是否安装vsftp

1
2
3
rpm -qa | grep vsftpd
# 若未安装,安装
yum -y install vsftpd

启动vsftpd服务

1
service vsftpd start

若无法启动,查看21端口是否被其他进程占用。若被占用,杀掉。

1
netstat -ntulp |grep 21

kill PID号

配置vsftpd

查看vsftpd位置

1
2
# whereis vsftpd
vsftpd: /usr/sbin/vsftpd /etc/vsftpd /usr/share/man/man8/vsftpd.8.gz

默认配置文件: /etc/vsftpd.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#     下面是配置的选项及说明
 ######### 核心设置 ###########
 # 允许本地用户登录
local_enable=YES
 # 本地用户的写权限
write_enable=YES
 # 使用FTP的本地文件权限,默认为077
# 一般设置为022
local_umask=022
 # 切换目录时
# 是否显示目录下.message的内容
dirmessage_enable=YES
dirlist_enable = NO
 #验证方式
#pam_service_name=vsftpd
 # 启用FTP数据端口的数据连接
connect_from_port_20=YES
 # 以独立的FTP服务运行
listen=yes
 # 修改连接端口
#listen_port=2121
 ######### 匿名登录设置 ###########
 # 允许匿名登录
anonymous_enable=NO
 # 如果允许匿名登录
# 是否开启匿名上传权限
#anon_upload_enable=YES
 # 如果允许匿名登录
# 是否允许匿名建立文件夹并在文件夹内上传文件
#anon_mkdir_write_enable=YES
 # 如果允许匿名登录
# 匿名帐号可以有删除的权限
#anon_other_write_enable=yes
 # 如果允许匿名登录
# 匿名的下载权限
# 匿名为Other,可设置目录/文件属性控制
#anon_world_readable_only=no
 # 如果允许匿名登录
# 限制匿名用户传输速率,单位bite
#anon_max_rate=30000
 ######### 用户限制设置 ###########
#### 限制登录
 # 用userlist来限制用户访问
#userlist_enable=yes
 # 名单中的人不允许访问
#userlist_deny=no
 # 限制名单文件放置的路径
#userlist_file=/etc/vsftpd/userlist_deny.chroot
 #### 限制目录
 # 限制所有用户都在家目录
#chroot_local_user=yes
# 调用限制在家目录的用户名单
chroot_list_enable=YES
# 限制在家目录的用户名单所在路径
chroot_list_file=/etc/vsftpd/chroot_list
 ######### 日志设置 ###########
 # 日志文件路径设置
xferlog_file=/var/log/vsftpd.log
 # 激活上传/下载的日志
xferlog_enable=YES
 # 使用标准的日志格式
#xferlog_std_format=YES
######### 安全设置 ###########
 # 用户空闲超时,单位秒
#idle_session_timeout=600
 # 数据连接空闲超时,单位秒
#data_connection_timeout=120
 # 将客户端空闲1分钟后断开
#accept_timeout=60
 # 中断1分钟后重新连接
#connect_timeout=60
 # 本地用户传输速率,单位bite
#local_max_rate=50000
 # FTP的最大连接数
#max_clients=200
 # 每IP的最大连接数
#max_per_ip=5
######### 被动模式设置 ###########
# 是否开户被动模式
pasv_enable=yes
 # 被动模式最小端口
pasv_min_port=5000
 # 被动模式最大端口
pasv_max_port=6000
######### 其他设置 ###########
 # 欢迎信息
ftpd_banner=Welcome to Ftp Server!

添加ftp防火墙规则

1
2
3
/sbin/iptables -I INPUT -p tcp --dport 21 -j ACCEPT
/etc/rc.d/init.d/iptables save
/etc/init.d/iptables restart

添加用户

1
useradd -d /home/ftp -s /sbin/nologin ftpuser

PS

不能用root用户登录,因为/etc/vsftpd/ftpusers ,/etc/vsftpd/user_list 两个列表中有root的名字,如需要用root的名字登录,请注释掉。(一般不建议这样做因为vsftpd密码是以明文方式传输的)

CentOS服务器图形化界面安装及XAMPP部署

使用PuTTY远程连接到终端

官网 下载PuTTY,打开Putty,并输入主机名或IP,点击Open。
之后会切换到另一个窗口,显示提示login as:,这时输入用户名(如管理员用户名root),按下回车键出现输入密码提示,直接键入密码按下回车键就登录成功了。

CentOS6.5安装vncserver实现图形化访问

安装gnome图形化桌面

1
2
3
#yum groupinstall -y "X Window System"
#yum groupinstall -y "Desktop"
#yum groupinstall -y "Chinese Support"

安装vncserver并配置

安装vncserver

1
#yum install -y tigervnc-server

配置vncserver

配置为开机自启动

1
#chkconfig --level 345 vncserver on

配置vnc密码

1
2
3
4
5
6
7
8
9
10
[root@sunth ~]# vncserver
You will require a password to access your desktops.
Password:
Verify:
xauth: creating new authority file /root/.Xauthority
xauth: (stdin):1: bad display name "sunth.localdomain:1" in "add" command
New 'sunth.localdomain:1 (root)' desktop is sunth.localdomain:1
Creating default startup script /root/.vnc/xstartup
Starting applications specified in /root/.vnc/xstartup
Log file is /root/.vnc/sunth.localdomain:1.log

配置为使用gnome桌面

修改 /root/.vnc/xstartup文件,把最后的 twm & 删掉 加上 gnome-session &。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/bin/sh
[ -r /etc/sysconfig/i18n ] && . /etc/sysconfig/i18n
export LANG
export SYSFONT
vncconfig -iconic &
unset SESSION_MANAGER
unset DBUS_SESSION_BUS_ADDRESS
OS=`uname -s`
if [ $OS = 'Linux' ]; then
case "$WINDOWMANAGER" in
*gnome*)
if [ -e /etc/SuSE-release ]; then
PATH=$PATH:/opt/gnome/bin
export PATH
fi
;;
esac
fi
if [ -x /etc/X11/xinit/xinitrc ]; then
exec /etc/X11/xinit/xinitrc
fi
if [ -f /etc/X11/xinit/xinitrc ]; then
exec sh /etc/X11/xinit/xinitrc
fi
[ -r $HOME/.Xresources ] && xrdb $HOME/.Xresources
xsetroot -solid grey
xterm -geometry 80x24+10+10 -ls -title "$VNCDESKTOP Desktop" &
gnome-session &

配置vncserver启动后监听端口和环境参数

修改/etc/sysconfig/vncservers 文件添加以下内容

1
2
VNCSERVERS="1:root"
VNCSERVERARGS[1]="-geometry 1200x800"

重启vncserver服务

1
#service vncserver restart

允许root访问图形界面和生成新的machine-id

1
2
#sed -i 's/.*!= root.*/#&/' /etc/pam.d/gdm
#dbus-uuidgen >/var/lib/dbus/machine-id

关闭selinux和NetworkManager服务

检查selinux服务并关闭

1
#vi /etc/selinux/config

确认里面的SELINUX字段的值是disabled,如果不是则改为disabled。

关闭NetworkManager服务

1
#chkconfig --del NetworkManager

测试登录:

在官网可以下载客户端VNC Viewer
VNC Server写上服务器的IP+编号(第二步,第四小步VNCSERVERS后面的数字)

部署安装XAMPP

准备工作

1
2
yum update
yum remove httpd*

升级系统,然后清理已有的环境以免出现安装冲突。

下载XAMPP

sourceforge 寻找合适的版本。下载。

1
2
cd /tmp
wget http://downloads.sourceforge.net/project/xampp/XAMPP%20Linux/1.8.3/xampp-linux-x64-1.8.3-4-installer.run

提权,安装

1
2
chmod 755 xampp-linux*
./xampp-linux*

安装完毕,启动XAMPP

1
/opt/lampp/lampp start

设置XAMPP

打开/opt/lampp/etc/httpd.conf文件,按需设置

P.S

  1. yum -y install firefox,使用firefox而不使用chrome是因为chrome用root开不了。
  2. 可以改一下putty的配色,原配色好难看。附连接

Serendipity Street




第一次听Chantal Chamberland的歌声时就被她沙哑沧桑的“摇滚腔”所吸引。一张爵士专辑,用如此“摇滚味”的嗓音来演绎,确实特别,也确实好听。
年轻时演唱摇滚乐的Chantal Chamberland。在2002年推出第一张爵士老歌翻唱专辑《This Is Our Time》。这张《Serendipity Street》是Chantal Chamberland的第二张专辑,标准的爵士三重奏,再加上Chantal Chamberland自己的吉他伴奏,配器简单但是感觉十足。顺便说一句,专辑中用的是Acoustic Bass,但录出来的效果却是电Bass的感觉。

专辑中大多数都是《Cry Me A River》,《God Bless the Child》之类的老歌,老时光的味道,甚至连Chantal Chamberland自己写的两首原创《It’s You》与《Across the Room》都有一种Old Times的感觉。《 Les Feuilles Mortes 》一曲更是好听得让人感动,Chantal Chamberland独特的沙哑嗓音演绎着这首六十年前的法国香颂,让人仿佛回到了老时光里的巴黎。当然大家更加熟悉这首歌的英文版——《Autumn Leaves》。

Les feuilles mortes(意为 “the dead leaves”,枯叶)是作曲家Joseph Kosma(1905-1969)在1945年于法国创作的香颂。1946年,法国歌手,影星Yves Montand(1921.10.13-1991.11.09 )在影片” LesPortes de la Nuit《夜之门》”中,演唱了这首歌。但这部电影在当时并不成功,虽然小有知名度的Yves Montand当时已经和Edith Piaf 在一起,但是Montand也没有因为这部电影以及这首主题曲而受人注目。直到1948年,已经走红的Yves Montand 在友人引荐下认识了诗人Jacques Prévert。Yves Montand 非常赏识Jacques的才华,便邀请 Jacques Prévert重新填词,Yves Montand 再次演唱,终于使这首经典流芳百世。1950年,美国音乐人John Herndon Mercer(1909-1976)将”Les Feuilles Mortes”改写成英文版”Autumn Leaves”。
值得一提的是,这首歌的录音很棒。我记得貌似刘汉盛榜单也有这张专辑。
这张CD的音质非常好,如果您没有感觉纯净的美感,第一个就错了。第二个吸引人的地方是低频,此处的低频软Q又有弹性,但量感不会太多,就好像我们吃了肥瘦分布均匀的雪花肥牛一般,或像是鲔鱼大肚肉,既带着诱人的油花,入口却是爽口的。这么迷人的低频哪里来的呢?是恰恰好的脚踩大鼓噗噗声与Bass的巧妙混合。第三个吸引人的地方当然是女歌手Chantal的嗓音。她的嗓音宽松不紧绷,声音线条饱满庞大,壮硕还带着磁性,很是迷人。假若您听到的是紧绷不够温暖、不够柔软、不够壮硕的嗓音,那就错了。

关于WordPress上传图片权限问题

今天WordPress不知出现什么毛病,上传的图片都没有访问的权限。
跑去服务器看下刚上传的文件。妈蛋居然没有权限。



奇怪的是,自动生成的缩略图居然有权限。



Uploads文件夹我是赋予写入跟修改权限的,图片可以上传,可是上传上去的图片IIS没有读取的权限。这是什么鬼。
注意到,新上传的图片虽然IIS_IUSRS没有读取权限。但是just_somethings这个组有很高的权限。just_somethings这个是什么鬼?这要涉及到“应用程序池标识”了。
查看巨硬的文档,找到如下信息:

应用程序池的标识是运行应用程序池的工作进程所使用的服务帐户名称。 默认情况下,应用程序池以 Network Service 用户帐户运行,该帐户拥有低级别的用户权限。 您可以将应用程序池配置为以 Windows Server® 2008 操作系统中的内置用户帐户之一运行。 例如,您可以指定 Local System 用户帐户,此帐户与 Network Service 或 Local Service 内置用户帐户相比,具有更高级别的用户权限。 但请注意,以具有高级别用户权限的帐户运行应用程序池存在严重的安全风险。
这么说来,just_somethings这个组就是“运行应用程序池的工作进程所使用的服务帐户名称”
OK解决方法来了,到Uploads目录下,把匿名身份验证改为“应用程序池标识”。
搞定



谈谈设计模式——抽象工厂模式

情景

从前,有一家叫做骚尼大法(简称大法)的公司,有富土康一个生产企业。富土康开了两条生产线,手机生产线跟电视生产线。大法跟富土康这两个好基友一直相处得很好,直到某一天来了个大米。
大米跟大法有样学样生产其手机来,那么现在富土康的手机生产线跟电视生产线就生产大米跟大法的产品了。我们看图。

UML类图



大米(DaMi)跟大法(DaFa)是两个抽象产品,而大米手机(DaMi_Phone)大米电视(DaMi_TV),大法手机(DaFA_Phone)大法电视(DaFA_TV)是对像个抽象产品的具体分类实现。富土康(Futukang)是抽象工厂接口,它里面包含所有产品创建的抽象方法。Futukang_Phone,Futukang_TV就是具体的工厂。
通常是运行时创建一个具体工厂(ConcreteFactory),这个具体工厂再创建特定实现的产品对象(ConcreteProduct)。为创建不同的产品对象,客户端对应不同的具体工厂。

实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#include <iostream>
using namespace std;
class DaFa
{
public:
virtual void Show() = 0;
};
class DaFa_Phone : public DaFa
{
public:
void Show()
{
cout<< "I'm DaFa_Phone"<<endl;
}
};
class DaFa_TV : public DaFa
{
public:
void Show()
{
cout<< "I'm DaFa_TV"<<endl;
}
};
class DaMi
{
public:
virtual void Show() = 0;
};
class DaMi_Phone : public DaMi
{
public:
void Show()
{
cout<< "I'm DaMi_Phone"<<endl;
}
};
class DaMi_TV : public DaMi
{
public:
void Show()
{
cout<< "I'm DaMi_TV"<<endl;
}
};
class Futukang
{
public:
virtual DaMi *CreateDaMi() = 0;
virtual DaFa *CreateDaFa() = 0;
};
class Futukang_Phone : public Futukang
{
public:
DaMi *CreateDaMi()
{
return new DaMi_Phone ();
}
DaFa *CreateDaFa()
{
return new DaFa_Phone ();
}
};
class Futukang_TV : public Futukang
{
public:
DaMi *CreateDaMi()
{
return new DaMi_TV ();
}
DaFa *CreateDaFa()
{
return new DaFa_TV ();
}
};
int main(int argc , char *argv [])
{
Futukang *factory_phone = new Futukang_Phone ();
DaMi *dami_phone = factory_phone->CreateDaMi();
DaFa *dafa_phone = factory_phone->CreateDaFa();
dami_phone->Show();
dafa_phone->Show();
Futukang *factory_tv = new Futukang_TV ();
DaMi *dami_tv = factory_tv->CreateDaMi();
DaFa *dafa_tv = factory_tv->CreateDaFa();
dami_tv->Show();
dafa_tv->Show();
if (factory_phone != NULL)
{
delete factory_phone;
factory_phone = NULL;
}
if (factory_tv != NULL)
{
delete factory_tv;
factory_tv = NULL;
}
if (dami_phone != NULL)
{
delete dami_phone;
dami_phone = NULL;
}
if (dafa_phone != NULL)
{
delete dafa_phone;
dafa_phone = NULL;
}
if (dami_tv != NULL)
{
delete dami_tv;
dami_tv = NULL;
}
if (dafa_tv != NULL)
{
delete dafa_tv;
dafa_tv = NULL;
}
return 0;
}

优点:

  • 跟工厂方法模式相比,少创建了对象。容易维护管理。
  • 让具体的创建实例的过程与客户端分离,产品的具体类名也被具体工厂的实现分离。
  • 适用于产品种类结构多的场合,主要用于创建一组(有多个种类)相关的产品,为它们提供创建的接口

缺点:

  • 新增一个Product的时候将会变得异常麻烦

情景2:

大米跟大法在富士康相处得很好,可是某一天来了个诺基鸭。除了要增加个诺基鸭,诺基鸭手机,诺基鸭电视三个类之外。还要弄个诺基鸭砖头。还要把富土康,富土康手机,富土康电视都修改一遍。太麻烦了,大米跟大法两个不干了,决定重新修改。

实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#include <iostream>
using namespace std;
class DaFa
{
public:
virtual void Show() = 0;
};
class DaFa_Phone : public DaFa
{
public:
void Show()
{
cout<< "I'm DaFa_Phone"<<endl;
}
};
class DaFa_TV : public DaFa
{
public:
void Show()
{
cout<< "I'm DaFa_TV"<<endl;
}
};
class DaMi
{
public:
virtual void Show() = 0;
};
class DaMi_Phone : public DaMi
{
public:
void Show()
{
cout<< "I'm DaMi_Phone"<<endl;
}
};
class DaMi_TV : public DaMi
{
public:
void Show()
{
cout<< "I'm DaMi_TV"<<endl;
}
};
class Nuojiya
{
public:
virtual void Show() = 0;
};
class Nuojiya_Phone : public Nuojiya
{
public:
void Show()
{
cout<< "I'm Nuojiya_Phone"<<endl;
}
};
class Nuojiya_TV : public Nuojiya
{
public:
void Show()
{
cout<< "I'm Nuojiya_TV"<<endl;
}
};
class Nuojiya_Brick:public Nuojiya
{
public:
void Show()
{
cout<< "I'm Nuojiya_Brick"<<endl;
}
};
class Futukang
{
private:
char item;
public:
Futukang(char _item){item=_item;}
DaFa* CreateDaFa(){
DaFa* result=NULL;
switch(item){
case 'P':
result=new DaFa_Phone;
break;
case 'T':
result=new DaFa_TV;
break;
}
return result;
}
DaMi* CreateDaMi(){
DaMi* result=NULL;
switch(item){
case 'P':
result=new DaMi_Phone;
break;
case 'T':
result=new DaMi_TV;
break;
}
return result;
}
Nuojiya* CreateNuojiya(){
Nuojiya* result=NULL;
switch(item){
case 'P':
result=new Nuojiya_Phone;
break;
case 'T':
result=new Nuojiya_TV;
break;
case'B':
result=new Nuojiya_Brick;
break;
}
return result;
}
};
int main(int argc , char *argv [])
{
Futukang phone=Futukang('P');
Futukang tv=Futukang('T');
Futukang brick=Futukang('B');
DaFa* dafa_phone=phone.CreateDaFa();
DaFa* dafa_tv=tv.CreateDaFa();
DaMi* dami_phone=phone.CreateDaMi();
DaMi* dami_tv=tv.CreateDaMi();
Nuojiya* nuojiya_phone=phone.CreateNuojiya();
Nuojiya* nuojiya_tv=tv.CreateNuojiya();
Nuojiya* nuojiya_brick=brick.CreateNuojiya();
dafa_phone->Show();
dafa_tv->Show();
dami_phone->Show();
dami_tv->Show();
nuojiya_phone->Show();
nuojiya_tv->Show();
nuojiya_brick->Show();
return 0;
}

去除Futukang_TV,Futukang_Phone,Futukang。取而代之的是Futukang这一个类,用一个简单工厂模式来实现。当然,在这以后,无论外界发生什么事情都不关大米跟大法两家的事了,就算新增一个“蓝翔”,富土康再新增一条“挖掘机”的流水线,都不关他们的事了。只需要在Futukang新增一个方法,然后再客户端作相应的修改就行。达到了解耦的目的。

谈谈设计模式——建造者模式

情景

我们知道,电脑一般是由CPU,主板,内存,显卡,电源,硬盘几大零件注册。我们假设,这几大零件分为好中差三个等级。我们要用这些零件组装三台电脑,分别是服务器电脑,游戏电脑,普通办公电脑。

  • 服务器电脑,我们要求CPU,主板,内存,电源,硬盘为‘好’,显卡为‘一般’。
  • 游戏电脑,我们要求CPU,内存,显卡为‘好’。主板,电源,硬盘为‘一般’。
  • 普通办公电脑,我们要求CPU,内存,硬盘,电源为‘一般’。显卡为‘差’。

UML类图



  • PcBuilder,你可以把他理解成为戴尔,联想等电脑生产商。它是一个建造电脑的抽象类,是为创建Pc对象的各个部件指定的抽象接口。
  • ServerBuilder等三个ConcreteBuilder,他们是具体的建造者。实现PcBuilder接口,构造和装配各个部件。
  • Pc,当然是指具体的电脑了。
  • PcDirector,指挥者,是构建一个使用PcBuild接口的对象。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#include <iostream>
#include<string>
using namespace std;
typedef enum HardwareLEVELenum
{
DefaultLevel,
Bad,
Normal,
Good
}HardwareLEVEL;
class PC
{
public:
void SetPC(string level){_level=level;}
void SetCPU(HardwareLEVEL CPU){ _CPU = CPU; }
void SetGPU(HardwareLEVEL GPU){ _GPU = GPU; }
void SetMotherboard(HardwareLEVEL Motherboard){ _Motherboard = Motherboard; }
void SetHarddisk(HardwareLEVEL Harddisk){ _Harddisk = Harddisk; }
void SetRAM(HardwareLEVEL RAM){ _RAM = RAM; }
void SetPower(HardwareLEVEL Power){ _Power = Power; }
void ShowPC()
{
cout<<"I'm a "<<_level<<endl;
cout<<"HardWare Level(1~3)"<<endl;
cout<<"CPU:"<<_CPU<<endl;
cout<<"GPU:"<<_GPU<<endl;
cout<<"Motherboard:"<<_Motherboard<<endl;
cout<<"Harddisk:"<<_Harddisk<<endl;
cout<<"RAM:"<<_RAM<<endl;
cout<<"Power:"<<_Power<<endl;
cout<<endl;
}
private:
string _level;
HardwareLEVEL _CPU;
HardwareLEVEL _GPU;
HardwareLEVEL _Motherboard;
HardwareLEVEL _Harddisk;
HardwareLEVEL _RAM;
HardwareLEVEL _Power;
};
class Builder
{
public:
virtual void BuildPC(){}
virtual void BuildCPU(){}
virtual void BuildGPU(){}
virtual void BuildMotherboard(){}
virtual void BuildHarddisk(){}
virtual void BuildRAM(){}
virtual void BuildPower(){}
virtual PC *GetPC(){ return NULL; }
};
class ServerPCBuilder : public Builder
{
public:
ServerPCBuilder(){ _ServerPC = new PC(); }
void BuildPC(){_ServerPC->SetPC("ServerPC");};
void BuildCPU(){ _ServerPC->SetCPU(Good); }
void BuildGPU(){ _ServerPC->SetGPU(Normal); }
void BuildMotherboard(){ _ServerPC->SetMotherboard(Good); }
void BuildHarddisk(){ _ServerPC->SetHarddisk(Good); }
void BuildRAM(){ _ServerPC->SetRAM(Good); }
void BuildPower(){ _ServerPC->SetPower(Good); }
PC *GetPC(){ return _ServerPC; }
private:
PC *_ServerPC;
};
class GamePCBuilder : public Builder
{
public:
GamePCBuilder(){ _GamerPC = new PC(); }
void BuildPC(){_GamerPC->SetPC("GamePC");};
void BuildCPU(){ _GamerPC->SetCPU(Good); }
void BuildGPU(){ _GamerPC->SetGPU(Good); }
void BuildMotherboard(){ _GamerPC->SetMotherboard(Normal); }
void BuildHarddisk(){ _GamerPC->SetHarddisk(Normal); }
void BuildRAM(){ _GamerPC->SetRAM(Good); }
void BuildPower(){ _GamerPC->SetPower(Normal); }
PC *GetPC(){ return _GamerPC; }
private:
PC *_GamerPC;
};
class OfficePCBuilder : public Builder
{
public:
OfficePCBuilder(){ _OfficePC = new PC(); }
void BuildPC(){_OfficePC->SetPC("OfficePC");};
void BuildCPU(){ _OfficePC->SetCPU(Normal); }
void BuildGPU(){ _OfficePC->SetGPU(Bad); }
void BuildMotherboard(){ _OfficePC->SetMotherboard(Normal); }
void BuildHarddisk(){ _OfficePC->SetHarddisk(Normal); }
void BuildRAM(){ _OfficePC->SetRAM(Normal); }
void BuildPower(){ _OfficePC->SetPower(Normal); }
PC *GetPC(){ return _OfficePC; }
private:
PC *_OfficePC;
};
class Director
{
public:
Director(Builder *builder) { m_Builder = builder; }
void CreatePC();
private:
Builder *m_Builder;
};
void Director::CreatePC()
{
m_Builder->BuildPC();
m_Builder->BuildCPU();
m_Builder->BuildGPU();
m_Builder->BuildMotherboard();
m_Builder->BuildHarddisk();
m_Builder->BuildMotherboard();
m_Builder->BuildHarddisk();
m_Builder->BuildRAM();
m_Builder->BuildPower();
}
int main(int argc, char *argv[])
{
Builder *builderPC1 = new ServerPCBuilder();
Director directorPC1(builderPC1);
directorPC1.CreatePC();
PC *PC1 = builderPC1->GetPC();
Builder *builderPC2 = new GamePCBuilder();
Director directorPC2(builderPC2);
directorPC2.CreatePC();
PC *PC2 = builderPC2->GetPC();
Builder *builderPC3 = new OfficePCBuilder();
Director directorPC3(builderPC3);
directorPC3.CreatePC();
PC *PC3 = builderPC3->GetPC();
if (PC1 == NULL)
return 0;
if (PC2 == NULL)
return 0;
if (PC3 == NULL)
return 0;
PC1->ShowPC();
PC2->ShowPC();
PC3->ShowPC();
delete PC1;
PC1 = NULL;
delete builderPC1;
builderPC1 = NULL;
delete PC2;
PC2 = NULL;
delete builderPC2;
builderPC2 = NULL;
delete PC3;
PC1 = NULL;
delete builderPC3;
builderPC3 = NULL;
return 0;
};

建造者模式

建造者模式(Builder),将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

就上面那个例子,我们知道,一个复杂对象(电脑)的创建,通常是由许多子对象(硬件)所组成的。由于需求的不同,组成复杂对象(电脑)的子对象(硬件)也不同(例如上面例子的服务器电脑,游戏电脑,办公电脑)。但是,我们知道,无论需求怎么样变化,电脑还是由CPU等几大硬件组成的。建造者模式就提供了一种“封装机制”来将各个对象的变化隔离开,最终,组合成复杂对象的过程是不会变的。

使用建造者模式,用户只需指定需要建造的类型就可以得到他们,具体的建造的过程和细节就不需知道了。

优点

  • 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者。若需要新增一个电脑产品,只需再定义一个具体的建造者就行了。
  • 将建造代码与表示代码分离。

缺点

  • 要求所创建的产品一般具有较多的共同点。

Breakfast On The Morning Tram




列车上的早餐,薄煎饼,咖啡的气味似乎已经漂浮在空中。拉开窗帘,昨晚暴雨洗刷的天空似乎更明媚,阳光伴随Stacey Kent的歌声传进列车里。

Stacey Kent加入Blue Note公司的首张专辑,全片由她的萨克斯风手老公Jim Tomlinson担任制作和编曲,全片以吉他、键盘、贝斯、鼓和萨克斯风这样的小型五人组合搭配,清清淡淡的传统爵士伴奏。专辑中翻唱了Serge Gainsbourg的两首法语歌,这个精通英语 法语 意大利语的女人,当她唱起法国香颂时,我甚至不相信她来自纽约。首曲《Ice Hotel》一副早晨睁开朦胧睡眼醒来的感觉,似乎整首歌都是伴随着阳光跟咖啡的气息。翻唱Louis Armstrong的《What A Wonderful World》唱出了Stacey Kent个人的味道,听这首歌时你脑海里会浮现出旅行途中,列车外蓝天白云,绿树红玫,列车里婴儿的啼哭声,熙熙攘攘的人群。

South of the Border,West of the Sun




村上春树在《爵士群像》一书中曾提到,他一直以为《South of The Border》这首歌是Nat King Cole所唱,并且以此为基础写了《国境之南,太阳之西》这本小说,后来才发现Nat King Cole根本没唱过这首歌。巧合的是,村上春树在1992出版了这本书,而这张专辑同年12月完成的专辑。

爵士乐早期的Swing乐风一直是村上春树最喜爱的类型,参与这张专辑演出的三位大师正是爵士Swing乐派的顶尖老将。钢琴手Claude Williamson乐风承传Bud Powell,五十年代崛起至今一直是West Coast Jazz的重要人物;贝斯手Andy Simpkins同样崛起於五十年代,曾任Sarah Vaughan巡迴演唱乐手,至今依然活跃与洛杉矶地区;鼓手Albert “Tootie” Heath更曾与John Coltrane、J.J. Johnson、Dexter Gordon、Herbie Hancock等爵士巨匠共事。由这三位老当益壮的乐手所共同架构的钢琴三重奏,当然流露出浓郁纯正的Swing韵味。

专辑中除了收录《Star Crossed Lovers》、《Pretendo》、《As Time Goes By》等多首耳熟能详的Jazz standard,开头曲《South of The Border》轻松愉快;终曲《West of The Sun》则是浪漫抒情。周日午后一杯手冲咖啡,一边听着Jazz一边看着村上春树的小说,不失为一种极好的休闲方式。

Venus制作人原哲夫先生:“Venus爵士乐希望能够带给乐迷们如同坐在Jazz Club第一排中间座位听音乐那样的感动。因此,请大家在自己心爱的音响系统中聆听Venus,重现舞台的原貌。相信一定可以让您们享受到真正的Acoustic Jazz Sound!” 来自日本的Venus厂牌,成立于1992年,录制了为数众多的经典爵士名盘,其音乐层次丰富、充满渲染力,而能让听众如临现场的高水准录音品质也是Venus的一大特色,不少专辑都陆续推出了高音质HQCD版及黑胶版,旗下唱片更屡屡荣获日本专业爵士乐杂志Swing Journal金赏奖。原哲夫在Venus中投入了对爵士乐的热情,迄今,Venus已成为日本爵士乐界最受瞩目的厂牌之一。

|