Windows资源浏览器——动态代理(java)
最后更新于:2022-04-01 07:02:55
写这篇博客是为了解决《[Dota兄订餐——静态代理(java)](http://blog.csdn.net/xiaoxian8023/article/details/9261851)》 里最终的遗留问题。
想必大家都或多或少了解一些Dos命令,在调用外部程序时,经常会用到dos命令来完成。其中有一条万能的命令,就是用Explorer.exe来打开任意程序,就相当于直接双击该程序。
先给大家看一下我们要调用的外部资源,放到一个文件夹中,包括图片、音乐、视频、文本文档、word文档、还有其他文档,最后还会加上一个网址。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c53c4864487.jpg)
我把Dos命令写入到一个bat中,也放入同一个文件夹中,命令如下:
~~~
@echo off
rem 调用默认程序打开图片
explorer 1.jpg
rem 调用默认程序打开pdf
explorer 42种方法全面提升宝宝的智能.pdf
rem 调用默认程序打开快捷方式
explorer SSH视频.lnk
rem 调用默认程序打开文本文档
explorer X光下看腾讯.txt
rem 调用默认程序打开chm
explorer 电脑故障维修大全.chm
rem 调用默认程序打开mp4
explorer 黄梅戏女驸马谁料皇榜中状元选段.mp4
rem 调用默认程序打开flv
explorer 马云创业演讲.flv
rem 调用默认程序打开音乐
explorer 我会很诚实.mp3
rem 调用默认程序打开Word文档
explorer 验收标准V3.1.docx
rem 调用默认浏览器打开百度
explorer http://www.baidu.com
~~~
现在我们双击这个bat文件,系统会自动调用相对应的默认程序去打开相应的文件。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c53c487cd88.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c53c4893e8f.jpg)
在上面的例子中,每种格式的文件都对应一种默认的打开程序。而且这个默认程序是可以手动更换的。而Explorer就相当于这里面的动态代理。我们把具体的参数传递给代理Explorer,它会根据不同的文档格式,启动对应的默认程序,然后再执行相应的操作。当默认程序换了,就会启动新设定的默认程序。而且不仅仅局限于一类文件或程序,而是可以打开任意程序或文档。这就是动态代理模式的应用。
静态代理是一个代理类服务一个接口,且代理是针对于这个接口编写特定代码,其字节码是编译期生成。而动态代理的字节码(即编译后的class文件)在程序运行时由java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了简化了编程工作,而且提高了软件系统的可扩展性,因为Java反射机制可以生成任意类型的动态代理类。Java.lang.reflect包中的Proxy类和InvocationHandler接口提供了生成动态代理类的能力。
先说说Proxy类,它就是一个动态代理类,最终返回Proxy这个动态代理类所代理的接口类对象,使用的就是Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 这个方法。它有三个参数:
- ClassLoader loader----指定被代理对象的类加载器
- Class[] Interfaces----指定被代理对象所实现的接口
- InvocationHandler h----指定需要调用的InvocationHandler对象
实现InVocationHandler接口,必须实现invoke()方法,该方法就是Proxy这个动态代理类所代理的接口类的抽象方法的真实实现。它有三个参数:
- Object proxy-----代理类对象
- Method method-----被代理对象的方法(这里不是接口的抽象方法了,是具体的实现类中的方法)
- Object[] args-----该方法的参数数组
JDK中具体的动态代理类是怎么产生的呢?
1. 产生代理类$Proxy0类
执行了Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
将产生$Proxy0类,它继承Proxy对象,并根据第二个参数,实现了被代理类的所有接口,自然就可以生成接口要实现的所有方法了(这时候会重写hashcode,toString和equals三个方法),但是还没有具体的实现体;
2. 将代理类$Proxy0类加载到JVM中
这时候是根据Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)它的第一个参数----就是被代理类的类加载器,把当前的代理类加载到JVM中;
3. 创建代理类$Proxy0类的对象
调用的$Proxy0类的$Proxy0(InvocationHandler)构造函数,生成$Proxy0类的对象。参数就是Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)它的第三个参数。这个参数就是我们自己实现的InvocationHandler对象,我们知道InvocationHandler对象中组合加入了代理类所代理的接口类的实现类;所以,$Proxy0对象调用所有要实现的接口的方法,都会调用InvocationHandler对象的invoke()方法实现。
简单的说了一下JDK动态代理的实现原理。下面我们用代码把上面的例子实现一下:
代码结构图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c53c48c00e1.jpg)
其中【com.bjpowernode.pattern.explorer.doc包】
OfficeSoftWare.java
~~~
package com.bjpowernode.pattern.explorer.doc;
/**
* Office软件接口
*
* @author : Longxuan
* @group : tgb8
* @Version : 1.00
* @Date : 2013-7-8 下午08:09:55
*/
public interface OfficeSoftWare {
/**
* 打开文件
* @param filePath 文件路径
*/
public void openFile(String filePath);
/**
* 保存文件
* @param filePath 文件路径
*/
public void saveFile(String filePath);
//其他功能...
}
~~~
WinWord.java
~~~
package com.bjpowernode.pattern.explorer.doc;
/**
* WinWord软件
*
* @author : Longxuan
* @group : tgb8
* @Version : 1.00
* @Date : 2013-7-8 下午09:01:54
*/
public class WinWord implements OfficeSoftWare {
/**
* 打开文件
* @param filePath 文件路径
*/
@Override
public void openFile(String filePath) {
System.out.println("打开文档 "+filePath);
}
/**
* 保存文件
* @param filePath 文件路径
*/
@Override
public void saveFile(String filePath) {
System.out.println("保存文档 "+filePath);
}
/**
* 返回名称
* @return
*/
@Override
public String toString() {
return "WinWord软件";
}
}
~~~
【com.bjpowernode.pattern.explorer.image】包:
ImageSoftWare.java
~~~
package com.bjpowernode.pattern.explorer.image;
/**
* 图片显示软件接口
*
* @author : Longxuan
* @group : tgb8
* @Version : 1.00
* @Date : 2013-7-8 下午08:08:13
*/
public interface ImageSoftWare {
/**
* 显示图片
* @param imgPath 图片路径
*/
public void viewImg(String imgPath);
/**
* 删除图片
* @param imgPath 图片路径
*/
public void delImg(String imgPath);
//其他功能...
}
~~~
ImageView.java
~~~
package com.bjpowernode.pattern.explorer.image;
/**
* Windows 图片查看器
*
* @author : Longxuan
* @group : tgb8
* @Version : 1.00
* @Date : 2013-7-8 下午09:03:23
*/
public class ImageView implements ImageSoftWare {
/**
* 返回软件名称
* @return
*/
@Override
public String toString() {
return "Windows图片查看器";
}
/**
* 删除图片
* @param imgPath 图片路径
*/
@Override
public void delImg(String imgPath) {
System.out.println("删除图片 "+imgPath);
}
/**
* 显示图片
* @param imgPath 图片路径
*/
@Override
public void viewImg(String imgPath) {
System.out.println("显示图片 "+imgPath);
}
}
~~~
【com.bjpowernode.pattern.explorer.music】包:
MusicSoftWare.java
~~~
package com.bjpowernode.pattern.explorer.music;
/**
* 音乐软件接口
*
* @author : Longxuan
* @group : tgb8
* @Version : 1.00
* @Date : 2013-7-8 下午08:16:43
*/
public interface MusicSoftWare {
/**
* 加载歌曲
* @param songPath 歌曲路径
*/
public void loadSong(String songPath);
/**
* 播放歌曲
* @param songPath
*/
public void playSong(String songPath);
//其他功能...
}
~~~
QQMusicPlayer.java
~~~
package com.bjpowernode.pattern.explorer.music;
/**
* QQ音乐播放器
*
* @author : Longxuan
* @group : tgb8
* @Version : 1.00
* @Date : 2013-7-8 下午09:00:25
*/
public class QQMusicPlayer implements MusicSoftWare {
/**
* 加载歌曲
* @param songPath 歌曲路径
*/
@Override
public void loadSong(String songPath) {
System.out.println("加载歌曲 "+songPath);
}
/**
* 播放歌曲
* @param songPath
*/
@Override
public void playSong(String songPath) {
System.out.println("播放歌曲 "+songPath);
}
/**
* 返回名称
* @return
*/
@Override
public String toString() {
return "QQ音乐播放器";
}
}
~~~
TTPlayer.java
~~~
package com.bjpowernode.pattern.explorer.music;
/**
* 千千静听播放器
*
* @author : Longxuan
* @group : tgb8
* @Version : 1.00
* @Date : 2013-7-8 下午09:03:21
*/
public class TTPlayer implements MusicSoftWare {
/**
* 加载歌曲
* @param songPath 歌曲路径
*/
@Override
public void loadSong(String songPath) {
System.out.println("加载歌曲 "+songPath);
}
/**
* 播放歌曲
* @param songPath
*/
@Override
public void playSong(String songPath) {
System.out.println("播放歌曲 "+songPath);
}
/**
* 返回名称
* @return
*/
@Override
public String toString() {
return "千千静听播放器";
}
}
~~~
【com.bjpowernode.pattern.explorer】包:
ExplorerHandler.java
~~~
package com.bjpowernode.pattern.explorer;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* jdk动态代理代理类
*
* @author : Longxuan
* @group : tgb8
* @Version : 1.00
* @Date : 2013-7-9 下午02:46:03
*/
public class ExplorerHandler implements InvocationHandler{
/**
* 目标对象
*/
private Object tagObj = null;
/**
* 通过反射,获取目标对象的代理对象
* @param tagObj 目标对象
* @return
*/
public Object newProxyInstance(Object tagObj){
this.tagObj = tagObj;
return Proxy.newProxyInstance(tagObj.getClass().getClassLoader(), tagObj.getClass().getInterfaces(), this);
}
/**
* 通过代理对象,调用方法
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("\n---->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
//获取文档类型
String fileType = args[0].toString().substring(args[0].toString().lastIndexOf(".")+1);
System.out.println("您正在操作的文档类型为:【" + fileType + "】,需要用【" + tagObj.toString() + "】打开");
System.out.print("执行操作--->>>");
//调用方法,并获取执行结果
Object res = method.invoke(tagObj, args);
//返回结果返回值
return res;
}
}
~~~
Client.java
~~~
package com.bjpowernode.pattern.explorer;
import com.bjpowernode.pattern.explorer.doc.OfficeSoftWare;
import com.bjpowernode.pattern.explorer.doc.WinWord;
import com.bjpowernode.pattern.explorer.image.ImageSoftWare;
import com.bjpowernode.pattern.explorer.image.ImageView;
import com.bjpowernode.pattern.explorer.music.MusicSoftWare;
import com.bjpowernode.pattern.explorer.music.QQMusicPlayer;
import com.bjpowernode.pattern.explorer.music.TTPlayer;
/**
* 客户端测试动态代理
*
* @author : Longxuan
* @group : tgb8
* @Version : 1.00
* @Date : 2013-7-9 下午02:45:06
*/
public class Client {
public static void main(String[] args) {
//实例化一个jdk动态代理代理对象
ExplorerHandler explorer = new ExplorerHandler();
//实例化一个window图片查看器的代理对象
ImageSoftWare isw = (ImageSoftWare) explorer.newProxyInstance(new ImageView());
//通过代理对象,执行显示图片
isw.viewImg("1.jpg");
//实例化一个Word的代理对象
OfficeSoftWare osw = (OfficeSoftWare) explorer.newProxyInstance(new WinWord());
//通过代理,执行显示图片
osw.openFile("验收标准V3.1.docx");
//实例化一个QQ音乐播放器的代理对象
MusicSoftWare msw = (MusicSoftWare) explorer.newProxyInstance(new QQMusicPlayer());
//通过代理对象,执行加载歌曲
msw.loadSong("我会很诚实.mp3");
System.out.println("\n---->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
System.out.println("切换音乐默认程序为【 千千静听】");
//实例化一个千千静听的代理对象
msw = (MusicSoftWare) explorer.newProxyInstance(new TTPlayer());
//通过代理对象,执行播放歌曲
msw.playSong("我会很诚实.mp3");
}
}
~~~
运行结果:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c53c48d4cad.jpg)
有了动态代理,在面对100个不同接口时,不用再像静态代理中编写100个特定的代理类。正如上面刚刚说过的,在动态代理模式中,只有一个动态代理类,每一个具体的代理对象都是在运行期生成的。这样不光节省了编码时间,最重要的是减少了维护的工作量和难度。比如就像例子中的,我需要在执行代码前,统一有一个操作(检验操作的文件类型),只要在代理类的invoke方法中编写一遍就可以了,而在静态代理中,需要给每个代理类都需要编写这段代码。在后期的更改中,也需要对每个代理类修改。现在换成动态代理,只需改一处即可。时间少,效率高,真是程序员必备之“良药”呀!
简单的介绍了一下动态代理,如果想有更为详细的了解,请移步《[Java 动态代理机制分析及扩展,第 1 部分](http://www.ibm.com/developerworks/cn/java/j-lo-proxy1/)》 和《[Java 动态代理机制分析及扩展,第 2 部分](http://www.ibm.com/developerworks/cn/java/j-lo-proxy2/)》
Dota兄订餐——静态代理(java)
最后更新于:2022-04-01 07:02:52
理解并使用设计模式,能够培养我们良好的面向对象编程习惯,同时在实际应用中,可以如鱼得水,享受游刃有余的乐趣。
代理模式是比较有用途的一种模式,而且变种较多,应用场合覆盖从小结构到整个系统的大结构,Proxy是代理的意思,我们也许有代理服务器等概念,代理概念可以解释为:在出发点到目的地之间有一道中间层,意为代理。
代理模式的定义是:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。它可以为委托对象预处理消息,过滤消息并转发消息,以及进行消息被委托对象执行后的后续处理,
或许有人会疑惑,为什么不直接用呢,非得需要代理呢?我们看下面一个场景:
"商女不知亡国恨;一天到晚打Dota;举头望明月,低头打Dota;洛阳亲友如相问,就说我在打Dota;少壮不努力,老大打Dota;垂死病中惊坐起,今天还没打Dota;生当作人杰,死亦打Dota;人生自古谁无死,来生继续打Dota。。。"
这就是当今大学男生宿舍的概况。小蔡是XX大学的计算机系大四学生,上面说的“歌谣”,也是他的大学生涯的真是写照。人称“小菜”,技术也真是菜到了家,不过却是“越挫越勇”,一天到晚打Dota,吃饭也不离开宿舍,一个电话搞定。这不,又来了:
啪的一拍键盘,“呼,终于赢了,差点让对方给办了,幸好对方的幽鬼被女朋友给拽走了。。。”,一看时间,“呀,都11点40了,不知不觉中又该吃饭啦”,嘀咕了一声,在被子里摸索了半天,抄起“王牌手机”诺基亚1100,拨通了那个无比熟悉的号码。。。
“喂,饭(范)哥,我是蔡哥呀。。。。嗯,对,对,还是Dota套餐,好,快点呀,挂了!”
范哥也是大四学生,从大一就开始做“代理订餐”的业务,已经有不小的“业绩”和很好的口碑了,人称“饭哥”。
10分钟,饭到,5分钟饭毕,然后菜哥又开始他伟大的Dota生涯了。。。。
情景结束。
在上面的情景中,食堂卖饭,范哥也卖饭,不过范哥卖的却是从食堂倒手而来的,小菜委托范哥去买,就相当于去食堂买饭了。这就是一个典型的代理模式。我们用代码和图说明一下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c53c4843fd8.jpg)
源码:SellMeal.java 售饭接口
~~~
package com.bjpowernode.pattern.bookmeal;
/**
* 售饭接口
* @author Longxuan
*
*/
public interface SellMeal {
/**
* 售饭方法
*/
public void sellRice();
}
~~~
Restaurant.java 餐厅类
~~~
package com.bjpowernode.pattern.bookmeal;
/**
* 餐厅
* @author Longxuan
*
*/
public class Restaurant implements SellMeal {
/**
* 餐厅售饭
*/
@Override
public void sellRice() {
System.out.println("1份米饭,2份菜(凉+热),1份鸡蛋汤。");
}
}
~~~
SellMealProxy.java 订餐代理类
~~~
package com.bjpowernode.pattern.bookmeal;
/**
* 订餐代理类,饭哥
* @author Longxuan
*
*/
public class SellMealProxy implements SellMeal {
private SellMeal sellMeal = null;
public SellMealProxy(SellMeal sellMeal){
this.sellMeal = sellMeal;
System.out.println("您好,我是订餐的范哥:");
}
/**
* 订餐售饭
*/
@Override
public void sellRice() {
System.out.println("这是您的Dota套餐:");
sellMeal.sellRice();
System.out.println("欢迎您的惠顾。");
}
}
~~~
DotaGG.java 客户端类,订饭的Dota兄
~~~
package com.bjpowernode.pattern.bookmeal;
/**
* 客户端类,订饭的Dota兄
* @author Longxuan
*
*/
public class dotaGG {
public static void main(String[] args){
//找代理食堂订饭的范哥
SellMealProxy FanGG = new SellMealProxy(new Restaurant());
//向范哥订饭
FanGG.sellRice();
}
}
~~~
运行结果:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cf33466a.jpg)
从源码中可以看到,客户端通过代理访问到目标对象,从而避免了与目标对象的直接接触。所以Dota兄小菜不用自己去买饭,就可以吃到食堂的饭菜了,同时节省了时间。而范哥赚了点money,虽然不多,但是贵在“薄利多销”,而且在每次送餐,都有在不经意间打出广告。客户多自然利润多,也就有了其存在的价值。
也就是说,我们不是非得直接让原对象去访问目标对象,有时候那样并不好,不合适,或者现实生活中不可能出现那种情况。所以我们需要一个代理类,去帮助我们完成这些操作,而且这些代理还可以对消息预处理和后继处理。这就是代理存在的意义。
如果你细心些,会发现一个代理类服务一个接口。即有100个不同的目标对象,则需要用到100个接口和对应的代理类,这也是静态代理的弊端。如何解决这个弊端呢?请期待我的[下一篇博文](http://blog.csdn.net/xiaoxian8023/article/details/9274359)。
状态模式教你学会清蒸鲤鱼
最后更新于:2022-04-01 07:02:50
买了九妹已经快有一年了,从经历了android2.3.5到深度优化的flyme1.1.3,风风雨雨,伴随着我一起走过。而我对魅族手机的设计也颇为留意。魅族是一个学习型的团队,很为用户考虑。今天要讲的魅族闹钟的新功能——事件定时器。以前看到了,只感觉很实用,近段时间在复习设计模式的时候,突然发现,魅族的这个新功能,尽然就是状态模式的真实实例。
为了让了解的和不了解的状态模式的同行们都看得懂我接下来说的,我先简要的介绍一下这个状态模式:
### 状态模式 State
状态模式允许一个对象在其内部状态改变时改变它的行为,让对象看起来似乎修改了它的类。
状态模式就是把系统的多个状态分割开来,分布到State的子类中,消除了庞大的分支语句,减少了耦合,同时也很容易增加新的状态的和转换。
这就相当于一个Schedule或School Timetable一样,时间的改变,我们的行程或课程也随之改变。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cf16523f.gif)
今天借助M9的新功能,跟大家来实验一下用状态模式做一次清蒸鲤鱼,让你在学习设计模式的时候,还另有收获。别的不多说,先上图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cf2c171b.png)
其中状态有5种:PrepareTime,BoillingState,SteamshState,TurnOffFishState,SaucingState,对应操作的五个步骤。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cf2d3605.png)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cf2e7908.png)
~~~
/// <summary>
/// 制作清蒸鱼
/// </summary>
public class SteamFish
{
private State current;//存储当前状态
public SteamFish()
{
current = new PrepareState();
}
//总计时
private int timecount;
//记录当前操作用时
private int time = 0;
public int TimeCount
{
get { return timecount; }
set
{
//记录当前操作用时
time = timecount;
timecount = value;
}
}
//任务完成标识
private bool finish = false;
public bool TaskFinished
{
get { return finish; }
set { finish = value; }
}
//切换状态
public void SetState(State s)
{
current = s;
}
//清蒸操作
public void SteamFishes()
{
//继续操作
current.Steam(this,time);
}
}
~~~
~~~
/// <summary>
/// 抽象状态
/// </summary>
public abstract class State
{
//清蒸鱼
public abstract void Steam(SteamFish sf,int time);
}
~~~
~~~
/// <summary>
/// 准备状态
/// </summary>
public class PrepareState : State
{
private static int statictime;//本状态累积所用时间(秒)
public override void Steam(SteamFish sf, int time)
{
statictime += (sf.TimeCount - time);//计算本状态累积所用时间(秒)
if (sf.TimeCount <= 60)
{
if (statictime == (sf.TimeCount - time))
{
Console.WriteLine("\r【清蒸鱼菜谱教程】\n\n①.将少许姜、盐、麻油放入鱼腹。" + "\t\t" + "所需时间 1分0秒 ,已用去 {0}分{1}秒。\n", statictime / 60, statictime % 60);
}
else
{
Console.WriteLine("\t\t\t\t\t亲,请稍等 ,已维持该状态 {0}分{1}秒。\n", statictime / 60, statictime % 60);
}
}
else
{
statictime = 0;
//下一步操作是:烧水
sf.SetState(new BoillingState());
sf.SteamFishes();
}
}
}
~~~
~~~
/// <summary>
/// 烧水状态
/// </summary>
public class BoillingState:State
{
private static int statictime;//本状态累积所用时间(秒)
public override void Steam(SteamFish sf, int time)
{
statictime += (sf.TimeCount - time);//计算本状态累积所用时间(秒)
if (sf.TimeCount <= 150)
{
if (statictime == (sf.TimeCount - time))
{
Console.WriteLine("②.往锅里放入适量的水并烧开。\t\t所需时间 1分30秒,已用去 {0}分{1}秒。\n", statictime / 60, statictime % 60);
}
else
{
Console.WriteLine("\t\t\t\t\t亲,请稍等 ,已维持该状态 {0}分{1}秒。\n", statictime / 60, statictime % 60);
}
}
else
{
statictime = 0;
//下一步操作是:煮鱼
sf.SetState(new SteamFishState());
sf.SteamFishes();
}
}
}
~~~
~~~
/// <summary>
/// 蒸鱼状态
/// </summary>
class SteamFishState:State
{
private static int statictime;//本状态累积所用时间(秒)
public override void Steam(SteamFish sf, int time)
{
statictime += (sf.TimeCount - time);//计算本状态累积所用时间(秒)
if (sf.TimeCount <= 570)
{
if (statictime == (sf.TimeCount - time))
{
Console.WriteLine("③.把鱼放入锅中,合上盖子。" + "\t\t" + "所需时间 7分0秒,已用去 {0}分{1}秒。\n", statictime / 60, statictime % 60);
}
else
{
Console.WriteLine("\t\t\t\t\t亲,请稍等 ,已维持该状态 {0}分{1}秒。\n", statictime / 60, statictime % 60);
}
}
else
{
statictime = 0;
//下一步操作是:关火
sf.SetState(new TurnOffState());
sf.SteamFishes();
}
}
}
~~~
~~~
/// <summary>
/// 关火状态
/// </summary>
class TurnOffState:State
{
private static int statictime;//本状态累积所用时间(秒)
public override void Steam(SteamFish sf,int time)
{
statictime += (sf.TimeCount - time);//计算本状态累积所用时间(秒)
if (sf.TimeCount <= 870)
{
if (statictime == (sf.TimeCount - time))
{
Console.WriteLine("④.关火,别打开盖!" + "\t\t\t" + "所需时间 5分0秒,已用去 {0}分{1}秒。\n", statictime / 60, statictime % 60);
}
else
{
Console.WriteLine("\t\t\t\t\t亲,请稍等 ,已维持该状态 {0}分{1}秒。\n", statictime / 60, statictime % 60);
}
}
else
{
statictime = 0;
//下一步操作是:烧水
sf.SetState(new SaucingState());
sf.SteamFishes();
}
}
}
~~~
~~~
/// <summary>
/// 调料状态
/// </summary>
class SaucingState:State
{
private static int statictime;//本状态累积所用时间(秒)
public override void Steam(SteamFish sf, int time)
{
statictime += (sf.TimeCount - time);//计算本状态累积所用时间(秒)
if (sf.TimeCount <= 880)
{
if (statictime == (sf.TimeCount - time))
{
Console.WriteLine("⑤.开盖,调料淋遍鱼身!" + "\t\t\t" + "所需时间 0分10秒,已用去 {0}分{1}秒。\n", statictime / 60, statictime % 60);
}
else
{
Console.WriteLine("\t\t\t\t\t亲,请稍等 ,已维持该状态 {0}分{1}秒。\n", statictime / 60, statictime % 60);
}
}
else
{
sf.TaskFinished = true;
Console.WriteLine("开抢了,再不抢就没了!");
}
}
}
~~~
客户端代码:
~~~
static void Main(string[] args)
{
//【清蒸鱼操作教程】
SteamFish sf = new SteamFish();
sf.TimeCount = 60;
sf.SteamFishes();
sf.TimeCount = 100;
sf.SteamFishes();
sf.TimeCount = 150;
sf.SteamFishes();
sf.TimeCount = 570;
sf.SteamFishes();
sf.TimeCount = 600;
sf.SteamFishes();
sf.TimeCount = 870;
sf.SteamFishes();
sf.TimeCount = 880;
sf.SteamFishes();
sf.TimeCount = 1000;
sf.SteamFishes();
Console.Read();
}
~~~
效果:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cf30c965.png)
常用的设计模式和代码
最后更新于:2022-04-01 07:02:48
设计模式是软件开发讨论中,亘古不变的话题,今天又拿出来说道两句,也是对设计模式的一个复习吧。
### 工厂方法模式
**工厂方法模型定义了一个用于创建对象的接口,让子类决定实例化哪一个类,工厂模式使一个类的实例化延迟到了其子类中**。工厂方法模式是优化的简单工厂模式,它很好的支持了“开闭原则”。每一个具体的工厂只能构建一个类的对象。具体工厂类与产品类是一对一的关系。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cef8238f.gif)
~~~
/// <summary>
/// 抽象产品类
/// </summary>
public class Product{
public Product(){
Console.Write("new Product");
}
}
/// <summary>
/// 具体产品类A
/// </summary>
public class ConcreteProductA:Product {
public ConcreteProduct(){
Console.Write("创建了一个 ConcreteProductA");
}
}
/// <summary>
/// 具体产品类B
/// </summary>
public class ConcreteProductB:Product {
public ConcreteProduct(){
Console.Write("创建了一个 ConcreteProductB");
}
}
/// <summary>
/// 抽象的创建者
/// </summary>
abstract public class Creator{
//抽象的工厂方法
public abstract Product FactoryMethod();
}
/// <summary>
/// 具体方法工厂A
/// </summary>
public class ConcreteCreatorA:Creator{
//返回一个产品A的对象
public override Product FactoryMethod(){
return new ConcreteProductA();
}
}
/// <summary>
/// 具体方法工厂B
/// </summary>
public class ConcreteCreatorB:Creator{
//返回一个产品B的对象
public override Product FactoryMethod(){
return new ConcreteProductB();
}
}
//client端
static void Main(string[] args)
{
Creator c = new ConcreteCreatorA();
Product p = c.FcatoryMethod();
c = new ConcreteCreatorB();
p = c.FcatoryMethod();
Console.ReadLine();
}
~~~
### 适配器模式Adapter
**适配器模式是将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。**
两个成熟的类需要通信,但是接口不同,由于开闭原则,我们不能去修改这两个类的接口,所以就需要一个适配器来完成衔接过程。
变压器就是很好的适配器模式的例子。用电设备所需要的电压是9V,但是电线上的电压却是220V的,我们不能去更改它们的电压输入或输出,所以我们用到了变压器。变压器是220V的输入,9V的输出。这样就可以将200V的电压变为9V的电压,用变压器将用电设备连接到了电线上工作了。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cefc996d.gif)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cefdf53d.gif)
上面两幅图中,都是Client端需要Request这个方法,但是Adaptee没有这个方法,所以就需要提供一个中间件/包装类(Wrapper)Adapter类来衔接。不同的是第一幅图Adapter继承自Adaptee,而第二幅图是在Adapter类中包装了一个Adaptee的实例。这就决定了第一幅图讲的是“类的结构模式”,而第二幅图则是“对象的结构模式”。
~~~
/// <summary>
/// 目标接口,客户所期待的接口。
/// </summary>
public class Target{
public virtual void Request(){
Console.Write("我是本系统中的普通请求.");
}
}
/// <summary>
/// 适配器,匹配2个接口不一致的类
/// </summary>
public class Adapter:Target{
private Adaptee adaptee = new Adaptee();
public void Request(){
adaptee.SpecificRequest();
}
}
/// <summary>
/// 源接口,与客户期待的接口不一致
/// </summary>
public class Adaptee(){
public void SpecificRequest(){
Console.Write("我是原有的真实调用的系统");
}
}
//client端
static void Main(string[] args)
{
Target t = new Adapter();
t.Request();
Console.ReadLine();
}
~~~
### 桥接模式 Bridge
**桥接模式将抽象部分与它的实现部分分离,使它们都可以独立地变化**。它很好的支持了开闭原则和组合及复用原则。实现系统可能有多角度分类,每一种分类都有可能变化,那么就把这些多角度分离出来让他们独立变化,减少他们之间的耦合。
2个相互耦合的系列,每个系列都有各自的产品变动。将这2个系列抽象成2个角色类,将各自的变化封装到对象的角色类中,然后再将2个角色类之间用组合的关系表示,这样就大大简化了使用类继承的复杂性,逻辑变得清晰了,易于扩展和维护。
桥接模式封装了变化,完成了解耦,实现了弱耦合。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cf0076c7.gif)
~~~
/// <summary>
/// 抽象部分
/// </summary>
public class Abstraction{
protected Implementor implementor;
public void SetImplementor(Implementor implementor){
this.implementor = implementor;
}
public virtual void Operation(){
implementor.OperationImp();
}
}
/// <summary>
/// 被提炼的抽象部分
/// </summary>
public class RefinedAbstraction:Abstraction{
public override void Operation(){
implementor.OperationImp();
}
}
/// <summary>
/// 实现部分
/// </summary>
abstract public class Implementor{
public abstract void OperationImp();
}
/// <summary>
/// 具体实现A
/// </summary>
public class conscreteImplementorA:Implementor{
pulic override void OperationImp(){
Console.Write("我是具体的A");
}
}
/// <summary>
/// 具体实现B
/// </summary>
public class conscreteImplementorB:Implementor{
pulic override void OperationImp(){
Console.Write("我是具体的B");
}
}
//client端
static void Main(string[] args)
{
Abstraction ab = new RefinedAbstraction();
ab.SetImplementor(new conscreteImplementorA());
ab.Operaton();
ab.SetImplementor(new conscreteImplementorB());
ab.Operaton();
Console.ReadLine();
}
~~~
具体的实例可以看我的另一篇博文《[面与卤的鹊桥相会——桥接模式](http://blog.csdn.net/xiaoxian8023/article/details/7642352)》
### 装饰模式 Decorator
**装饰模式动态地给一个对象添加一些额外的职责,就增加功能来说,它比生成子类更灵活**。也可以这样说,装饰模式把复杂类中的核心职责和装饰功能区分开了,这样既简化了复杂类,有去除了相关类中重复的装饰逻辑。装饰模式没有通过继承原有类来扩展功能,但却达到了一样的目的,而且比继承更加灵活,所以可以说装饰模式是继承关系的一种替代方案。装饰模式解耦了核心和装饰功能,所以也是强调了松耦合。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cf037df6.gif)
~~~
/// <summary>
/// 最高接口,被装饰者和“装饰品”都继承于此
/// </summary>
abstract public class Component{
public abstract void Operation();
}
/// <summary>
/// 具体的被装饰者
/// </summary>
public class Concretecomponent:Component{
public override void Operation(){
Console.Write("具体对象的装饰操作");
}
}
/// <summary>
/// 装饰类
/// </summary>
abstract public class Decorator:Component(){
protected Component component;
public void SetComponent(Component component){
this.component = component;
}
public override void Operation(){
if(component!=null) component.Operation();
}
}
/// <summary>
/// 具体的装饰类A
/// </summary>
public class ConcreteDecoratorA:Decorator{
private string addedState;
public override void Operation(){
base.Operation();
addedState="New State";
Console.Write("具体装饰A的操作(添加了新的状态)");
}
}
/// <summary>
/// 具体的装饰类B
/// </summary>
public class ConcreteDecoratorB:Decorator{
public override void Operartion(){
base.Operation();
AddedBehavior();
Console.WriteLine("具体装饰B的操作(添加了新的方法)");
}
private void AddedBehavior(){
//添加新的行为
}
}
//client端
static void Main(string[] args)
{
Concretecomponent c = new Concretecomponent();
Decorator d1 = new ConcreteDecoratorA();
Decorator d2 = new ConcreteDecoratorB();
d1.SetComponent(c);
d2.SetComponent(d1);//注意这里装饰的是d1,因为这里的d1是装饰了d1的c。
d2.Operation();
Console.ReadLine();
}
~~~
### 外观模式 Facade
**外观模式为子系统中的一组接口提供了一个一致的界面,此模式**定义了一个高层接口,这个接口使得这些子系统更加容易使用。**
外观模式中,客户对各个具体的子系统是不了解的,所以对这些子系统进行了封装,对外只提供了用户所明白的单一而简单的接口,用户直接使用这个接口就可以完成操作,而不用去理睬具体的过程,而且子系统的变化不会影响到用户,这样就做到了信息隐蔽。
这就相当于新生接待员。新生对入学流程不清楚,但是接待员学长可是明白的。学生跟着接待员就可以把各个手续办理完毕了。可以说**外观模式封装了细节**。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cf051a9c.gif)
~~~
/// <summary>
/// 高层的统一接口,封装了子系统繁杂的接口。
/// </summary>
public class Facade{
private SubSystemOne one;
private SubSystemTwo two;
private SubSystemThree three;
public Facade(){
one = new SubSystemOne();
two = new SubSystemTwo();
three = new SubSystemThree();
}
public void MethodA(){
one.MethodOne();
two.MethodTwo();
}
public void MethodB(){
one.MethodOne();
three.MethodThree();
}
}
/// <summary>
/// 子系统One。
/// </summary>
public class SubSystemOne{
public void MethodOne(){
Console.Write("我是One");
}
}
/// <summary>
/// 子系统Two
/// </summary>
public class SubSystemTwo{
public void MethodTwo(){
Console.Write("我是Two");
}
}
/// <summary>
/// 子系统Three
/// </summary>
public class SubSystemThree{
public void MethodThree(){
Console.Write("我是Three");
}
}
//client端
static void Main(string[] args)
{
Facade f1 = new Facade();
f1.MethodA();
Facade f2 = new Facade();
f2.MethodB();
Console.ReadLine();
}
~~~
### 策略模式 Strategy
**策略模式定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。本模式使得算法的变化不会影响到使用算法的客户。**
策略模式将每一个算法封装到一个具有公共接口的独立类中,解除了客户与具体算法的直接耦合,是客户改变算法更为容易。
策略模式+简单工厂+反射+配置文件可以组成更为灵活的方式。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cf186bd5.gif)
~~~
/// <summary>
/// 算法的公共接口
/// </summary>
abstract public class Strategy{
public abstract void AlgorithmInterface();
}
/// <summary>
/// 具体算法A
/// </summary>
public class ConcreteStrategyA:Strategy{
public override void AlgorithmInterface(){
Console.Write("具体算法A");
}
}
/// <summary>
/// 具体算法B
/// </summary>
public class ConcreteStrategyB:Strategy{
public override void AlgorithmInterface(){
Console.Write("具体算法B");
}
}
/// <summary>
/// 具体算法C
/// </summary>
public class ConcreteStrategyC:Strategy{
public override void AlgorithmInterface(){
Console.Write("具体算法C");
}
}
/// <summary>
/// 上下文,用来维护一个对Strategy对象的引用。
/// </summary>
public class Context{
private Strategy strategy;
public Context(Strategy strategy){
this.strategy=strategy;
}
//上下文接口
public void ContextInterface(){
strategy.AlgorithmInterface();
}
}
//client端
static void Main(string[] args)
{
Context context = new Context(new ConcreteStrategyA());
context.ContextInterface();
context = new Context(new ConcreteStrategyB());
context.ContextInterface();
context = new Context(new ConcreteStrategyC());
context.ContextInterface();
Console.ReadLine();
}
~~~
### 观察者模式 Observer
**观察者模式定义了对象间的一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖它的对象都会得到通知,并被自动更新,**
系统中有两个方面,其中一个方面依赖与另一个方面,我们把这两个方面抽象,是各自可以独立的变化和复用。
就像我们现在所用到的分层,不就是一层层的依赖么?还有系统组件升级,系统功能也跟着变化,这也属于观察者模式。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cf29730a.jpg)
~~~
/// <summary>
/// 抽象观察类
/// </summary>
abstract public class Observer{
public abstract void Update();
}
/// <summary>
/// 具体观察类
/// </summary>
public class Concreteobserver:Observer{
private string name;
private string observerState;
private ConcreteSubject subject;
public Concreteobserver(ConcreteSubject subject,string name){
this.subject=subject;
this.name= name;
}
public override void Update(){
observerState=subject.GetState();
Console.write("观察者{0}的新状态是{1}",name,observerState);
}
}
/// <summary>
/// 抽象主题类
/// </summary>
abstract public class Subject(){
private List<observer> observers = new List<observer>() ;
public void Attach(Observer observer){
observers.Add(Observer);
}
public void Detach(Observer Observer){
observers.Remove(Observer);
}
public void NotifyObservers(){
foreach(Observer o in observers){
o.Update();
}
}
}
/// <summary>
/// 具体主题类
/// </summary>
public class ConcreteSubject:Subject{
private string subjectState;
public string SubjectState{
get{return subjectstate;}
set{subjectstrate=value;}
}
public void GetState(){
return subjectstate;
}
}
//client端
static void Main(string[] args)
{
ConcreteSubject c = new ConcreteSubject();
Concreteobserver o1 = new Concreteobserver(c,"X");
Concreteobserver o2 = new Concreteobserver(c,"Y");
Concreteobserver o3 = new Concreteobserver((c,"Z");
c.Attach(o1);
c.Attach(o2);
c.Attach(o3);
c.subjectstate="abc";
c.Nofify();
Console.ReadLine();
}
~~~
欲了解其他模式,请查看我的这3篇博文:
《[设计模式之创建型模式](http://blog.csdn.net/xiaoxian8023/article/details/6933972)》
《[设计模式之结构型模式](http://blog.csdn.net/xiaoxian8023/article/details/6937589)》
《[设计模式之行为型模式](http://blog.csdn.net/xiaoxian8023/article/details/6943768)》
面与卤的鹊桥相会——桥接模式
最后更新于:2022-04-01 07:02:46
**本文来自:崔成龙博客专栏。转载请注明出处:**[**http://blog.csdn.net/xiaoxian8023**](http://blog.csdn.net/xiaoxian8023)
软考上午题终于考完了。三个赶考者都感觉不错。检查了2遍,提前30分钟都出来了 。
小A,小B,小C楼下碰头,相视一笑,轻松之感溢于言表。遂决定去吃面,以犒劳自己的肚子。
“老板,我要西红柿鸡蛋面!”,“尖椒炸酱面!”,“苏格兰打卤面!”。。。。。。“好嘞!”
面快出锅了,“哎哎,老板,怎么我的面跟他的面一样啊,就是换了一下卤?”往面里放卤的服务员翻了小B一眼,不搭理。旁边那个年长点的师傅笑了笑,说道“小兄弟儿,这你就不懂了吧。其实面都是一样的,只是换一下卤罢了。”“哦哦,原来如此啊。”小B灰溜溜得端起面来跑开了。。。
面毕,小B实在是咽不下这口气。就想在小C那儿“捞回点儿本来”。
“小C,面吃的怎么样啊?”小B阴阳怪气的问道。
小C看着小B的表情,觉得有点不对劲,自己是不是又要上当了,但是又想不出来自己哪留有什么“把柄”,然后就答道,
“还,还行吧,问这干嘛??”
“嘿,你怕什么呀。我考一个关于吃面的题目”
“嗨,我还以为什么呢,随便考吧,哥是来者不拒。”小C心里想到,就你那智商,能出什么样的难题呢,等着被鄙视吧。
“你感觉自家煮面和饭馆煮面有什么不同?”
“这还用问!感觉不同呗。自己煮面,自己累死了还得做饭,又是炒卤,又是煮面的,最低也得折腾半小时吧。饭馆多好啊,只要吆喝一声,人家都给你做好,端到你面前了。而且速度超快的。连这个都不知道。也对,就你那智商,好吧,我原谅你,哈哈哈哈”
“哼,别笑得太早了。同样是面?你说为什么感觉不同啊”
“呃,这个嘛,嗯,让我想想”小C装傻了。。。
“哈,有点意思哎。我给你说说看看对不。”小A憋不住了。接着说道,
“刚才小C说了,自家煮面,你必须自己做卤,自己煮面。每次想吃面的时候,这两步都是少不了的。但是面馆不一样啊。饭馆卤都是提前做好的。你点了面后,面馆给你煮面,然后加上你要的卤不就OK了吗!而且速度超快。比自己做饭省事多了。”
“哦~,原来如此。”
“我说那会那个服务员像看白痴的一样看我呀。”小B嘀咕道
“what,what!!原来你被吃瘪了啊,哈哈”小C得意得捧腹大笑。
小A眼睛一转,对即将要发飙的小B说道,
“既然是你提出来的,你说说饭馆为什么要这么做呢?”
“呃,嗯,,,,我想,应该是为了更快捷,方便地方便用户,同时提高自己的效率吧。”
“还有没有别的?”
小B想了半天,摇来摇头,“想不到了”
“提示一点,面馆在增加新类型面的时候,是怎么做的?”
“哦~~,我知道了,因为面差不多是一样的,只需要准备卤就行了。”
“嗯,对的。其实面也是有不同的。比如说宽面,拉面,圆面。这样一组合,新的类型的面就出炉了。”
“原来面馆里也有这么大学问啊。”
“那是当然。365行,行行出状元啊。先不说这个了,既然说道这个面了,那你用面向对象写写刚才我们讨论的这件事儿吧。”
“好。我试试吧”小B自信道。
既然是面向对象,那么先把类找出来吧。首先有原料(Material),下分为面(*Noodles*)和卤(*Halogen*)。面有宽面条(WideNoodles),窄面条(NarrowNoodles)等多种类型,卤菜有西红柿鸡蛋卤(TomatoAndEgg),茄子豆瓣酱(AubergineBeanPaste)等多种卤菜。 西红柿鸡蛋面类(TomatoEggNoodles)为西红柿鸡蛋卤和宽面构成,而苏格兰打卤面(ScotlandNoodles)则是有茄子 豆瓣酱和窄面构成的。
先画出UML图:
uml图1.0版
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cf226f54.png)
“看看怎么样啊”,小B得瑟的说道。
“你有没有考虑代码的实现呢?”小A用看白痴的眼神扫了小B一眼,“除了C++,还有几个面向对象的有多继承啊!”
“呃,这个,失误失误,我马上改!”
“别急啊,还有错误呢,西红柿鸡蛋面有两部分构成,那肯定是用聚合或者是组合啊,不应该用继承的”小A提醒道。
“哦,原来如此,我说怎么感觉有点不对劲呢,等着吧”
5分钟过后。。。
“哦,终于出来了,看看这次怎么样啊。”
uml图2.0.版
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cf23a438.png)
“西红柿鸡蛋面和苏格兰打卤面的组成关系也画出来了。嗯不错,不过还有一点不对劲。假如我现在加一个新类型的面——西红柿鸡蛋卤面(窄面),你怎么加”
“那就从面条类里泛化一个西红柿鸡蛋卤面(窄面)类,然后再在它上面加上与西红柿鸡蛋卤和窄面的组合关系”
“那如果我现在面馆开10个分面馆,每个分面馆新增10样类,你是不是这100种面都要重新添加一遍面和卤的组合关系吧。”
“那我还不得累死啊,这种活干不得!”小B惶恐的说道。
“不管累,当你画出UML图的时候,能把你乱死。成品面与具体的面类型和卤菜类型的耦合性就太高了。想办法松散这些耦合。”
“那怎么办呢?”小B焦急的问道。
“呃,提醒你一点,你先对比一下你这两版UML各自的特点。”
“第一幅图继承关系比较明显,第二幅图则利用了组合关系,使其继承关系简单化了”
“如果你把这两个图合成一个图呢?”
“这,这,怎么合啊。”
“看来还得我出马啊,看着昂”
UML图3.0版
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cf24a891.png)
“不管什么打卤面,都抽象于卤面类。卤面类由面条类和卤菜类组成。面条类和卤菜类都有各自的子类。”
“这个图好面熟啊。让我想想昂。哦,对了,这不是桥接模式吗!”
“是的,不过这样做有什么好处呢?”
“这样做有几个好处,第一,松散耦合。具体的打卤面不再与具体的面类型和卤类型直接关联,松散了它们之间的耦合;第二,在变化方面,具体的打卤面, 具体的面类型和具体的卤类型各自的变化,互不影响”
“这样我在开分面馆的时候就不用画那么多关系了,添加新类型的打卤面不再成为难事。果然很厉害。哈哈哈哈”小B得瑟的笑着,好像他真的要开面馆似的。
“对头!这就满足了开闭原则,不用去修改,只需要添加即可。实例化具体的打卤面时,在客户端指定一下要哪种面,哪个卤即可。”小A接着说,
“其实这里面最重要的是利用聚合组合关系,松散了耦合,使得抽象不再依赖于具体,而具体要依赖于抽象。”
“哦,对哦。设计模式果然牛X。”
“代码留在晚上再写吧,早点休息,下午还有考试呢!”小A看着一脸丫丫的小B,提醒道。
“哦哦,差点忘了还有考试了。希望下午碰到桥接模式,那我就。。。”小B继续陷入丫丫ing。
小A摇摇头,不再理睬小B,推开霸占自己床铺的小C,休息去了。
面条类及子类
~~~
#region 面条类及子类
/// <summary>
/// 面条类
/// </summary>
public abstract class Noodles
{
public Noodles()
{
}
/// <summary>
/// 获取面类型名称
/// </summary>
/// <returns></returns>
public abstract string GetName();
}
/// <summary>
/// 窄面条
/// </summary>
public class NarrowNoodles:Noodles
{
public NarrowNoodles()
{
}
public override string GetName()
{
return "窄面条";
}
}
/// <summary>
/// 宽面条
/// </summary>
public class WideNoodles : Noodles
{
public WideNoodles()
{
}
public override string GetName()
{
return "宽面条";
}
}
#endregion
~~~
卤菜类及子类
~~~
#region 卤菜类及子类
/// <summary>
/// 卤菜类
/// </summary>
public abstract class Halogen
{
public Halogen()
{
}
/// <summary>
/// 获取卤菜名称
/// </summary>
/// <returns></returns>
public abstract string GetName();
}
/// <summary>
/// 西红柿鸡蛋卤
/// </summary>
public class TomatoAndEgg : Halogen
{
public TomatoAndEgg()
{
}
public override string GetName()
{
return "西红柿鸡蛋卤";
}
}
/// <summary>
/// 茄子豆瓣酱
/// </summary>
public class AubergineBeanPaste:Halogen
{
public AubergineBeanPaste()
{
}
public override string GetName()
{
return "茄子豆瓣酱";
}
}
#endregion
~~~
打卤面类及子类
~~~
#region 打卤面类及子类
/// <summary>
/// 打卤面类
/// </summary>
public class NoodlesAndHalogen
{
protected string name;
protected Noodles noodles;
protected Halogen halogen;
public NoodlesAndHalogen(string name, Noodles noodles, Halogen halogen)
{
this.name=name;
this.noodles = noodles;
this.halogen = halogen;
}
/// <summary>
/// 获取名称及成分
/// </summary>
public void GetName()
{
Console.WriteLine("我是"+name+",由"+noodles.GetName () +"和"+halogen.GetName ()+"组成");
}
}
/// <summary>
/// 西红柿鸡蛋面
/// </summary>
public class TomatoEggNoodles : NoodlesAndHalogen
{
public TomatoEggNoodles(string name, Noodles noodles, Halogen halogen)
: base(name, noodles, halogen)
{
}
}
/// <summary>
/// 苏格兰打卤面
/// </summary>
public class ScotlandNoodles : NoodlesAndHalogen
{
public ScotlandNoodles(string name, Noodles noodles, Halogen halogen)
: base(name, noodles,halogen)
{
}
}
#endregion
~~~
客户端代码:
~~~
static void Main(string[] args)
{
NoodlesAndHalogen n1=new TomatoEggNoodles("西红柿鸡蛋面(宽面)",new WideNoodles(),new TomatoAndEgg() );
n1.GetName();
NoodlesAndHalogen n2 = new ScotlandNoodles("苏格兰打卤面(窄面)", new NarrowNoodles(), new AubergineBeanPaste());
n2.GetName();
Console.Read();
}
~~~
运行结果:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cf25f28c.jpg)
[![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c53c47e8b31.png)](http://my.csdn.net/my/favorite/miniadd?t=%25E9%259D%25A2%25E4%25B8%258E%25E5%258D%25A4%25E7%259A%2584%25E9%25B9%258A%25E6%25A1%25A5%25E7%259B%25B8%25E4%25BC%259A%25E2%2580%2594%25E2%2580%2594%25E6%25A1%25A5%25E6%258E%25A5%25E6%25A8%25A1%25E5%25BC%258F&u=http://blog.csdn.net/xiaoxian8023/article/details/7642352)
烧烤店里邂逅三层
最后更新于:2022-04-01 07:02:43
~~~
【时间:2012年5月25日 19:10 地点:北京西三环某宾馆楼下的烧烤店】
~~~
~~~
软考前一天,几个“进京赶考”的“准北漂者”,为了第二天考一个好成绩,一致决定杀向楼下烧烤店。
~~~
没到门口呢,小A抢先喊道“服务员,来只烤鸡翅,来串烤羊肉!”,小B一看自己喊晚了,处于“程序猿”的职业精神,直接喊了一句“Call A”。“小哥,在北京,要讲礼貌啊,不要说脏话!”服务员小李不慌不忙的说道。“啊,那个,刚才说的不是脏话,意思是“同上””,小B还自嘲一句“唉,程序猿的悲哀啊!”。。。服务员笑了笑,去通知烧烤老王了。这时磨叽的小C才到,看到一头郁闷的小B直接忽视,显然了他的性格。“服务员,来只烤鸡翅,来串烤羊肉!”。服务员小张“好的,请稍等。”,也去通知老王去了。
话说,三个程序猿干坐着等,那肯定是闲不住的。小B,憋不住了,
“话说,咱们这也是进京赶考,你们说明天的考试能不能碰到原题呢?”。
“每年都有一道相近的原题”。小A答道。
“可怜我的设计模式不怎么会呢,下午题怎么过啊。”
“学过三层吗?”
“学过啊,想当年,我学三层时,那可谓是。。。。哎呀,别打了,打傻了你们赔的起吗”。
“就你那智商,本来就是负”
“切,随便考,看看我会不会!”
“那你说说我们现在遇到的三层吧”一直不语的小C说道。
“不许说服务员-厨师-采购员这个版本。”小A抢先将了小B一军。
“说就说。who怕who啊。”,小B,嘴上强硬,心里实则有点发虚。沉下气来,仔细想了想。不理睬小A的中途捣乱,5分钟过后,“有了!”
“你有了?恭喜恭喜啊!什么时候出生??哈哈哈哈~”小A得瑟的笑着。
“去你的,说正题。话说,这个三层就拿我们做实验吧。我们三个就是表示层,刚才那两个服务员是业务逻辑层,而烤肉的师傅就是数据访问层”。顿了顿,继续说,“我们只管点烧烤和吃,服务员管通知,上菜,结算,而烤肉师傅则只负责烤肉”。每个角色,顾客换来换取,服务员也换,烤肉师傅也可以换,不管怎么变化,只要这个结构不变,就没有问题。
“这是您三位的烤羊肉和烤鸡翅,请慢用!”服务员小李把烧烤给端上来了。
“先吃先吃”,小A和小B争着抢大的吃。
“嗯,说的不错,回去把这个用代码实现了哈”,小C拿起剩余的那只鸡翅膀也啃了起来。。。
【时间:2012年5月25日 20:00 地点:北京西三环某宾馆】
“吃完了,该工作了”小C看到打算躺下看电视的小B说道。
“什么工作?”
“当然是写代码了,你不会又把任务给吃了吧”
“这小子肯定不会,你就放过他吧”小A讽刺道。
“谁说不会的,等着”。小B翻开自己的笔记本,开始噼里啪啦的敲打着。。。
3分钟过去后。。。
"写出来了,请看"
~~~
static void Main(string[] args)
{
Console.WriteLine("小A:来只烤鸡翅");
Console.WriteLine("服务员1:烤鸡翅一只");
Console.WriteLine("烤肉师傅:烤鸡翅");
Console.WriteLine("");
Console.WriteLine("小A:来只烤羊肉");
Console.WriteLine("服务员1:烤羊肉一串");
Console.WriteLine("烤肉师傅:烤羊肉");
Console.WriteLine("");
Console.WriteLine("小B:来只烤鸡翅");
Console.WriteLine("服务员1:烤鸡翅一只");
Console.WriteLine("烤肉师傅:烤鸡翅");
Console.WriteLine("");
Console.WriteLine("小B:来只烤鸡翅");
Console.WriteLine("服务员1:烤鸡翅一只");
Console.WriteLine("烤肉师傅:烤鸡翅");
Console.WriteLine("");
Console.WriteLine("小C:来只烤羊肉");
Console.WriteLine("服务员2:烤羊肉一串");
Console.WriteLine("烤肉师傅:烤羊肉");
Console.WriteLine("");
Console.WriteLine("小C:来只烤鸡翅");
Console.WriteLine("服务员2:烤鸡翅一只");
Console.WriteLine("烤肉师傅:烤鸡翅");
Console.WriteLine("");
Console.ReadLine();
}
~~~
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cf1e91ed.png)
“不是吧,过程式的代码啊,亏你还是考软考的人,真是给我丢脸哎!”一向不怎么鄙视别人的小C也禁不住开始发表感叹!
“他就是个吃货,把面向对象全吃了,哈哈”小A幸灾乐祸道。
“切,别着急啊,开个玩笑,这是先让你们看看最终的结果。等着啊,下面就是面向对象版的”
10分钟过去后。。
“啊,老天,经过扼杀了我N+1个脑细胞,终于做出来了”小B悲叹道。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cf20a289.png)
顾客类:
~~~
using Servers;
namespace User
{
/// <summary>
/// 顾客类
/// </summary>
class User
{
string name;
Servers.Servers server;
public User(string name)
{
this.name = name;
}
public void SetServer(string name)
{
this.server = new Servers.Servers(name);
}
public void Bake()
{
Console.WriteLine(name+":来只烤鸡翅");
server.KaoJiChi();
Console.WriteLine();
Console.WriteLine(name + ":来串烤羊肉");
server.KaoYangRou();
Console.WriteLine();
}
static void Main(string[] args)
{
User a = new User("顾客小A");
User b = new User("顾客小B");
a.SetServer("服务员小李");
b.SetServer("服务员小李");
a.Bake();
b.Bake();
User c = new User("顾客小C");
c.SetServer("服务员小张");
c.Bake();
Console.ReadLine();
}
}
}
~~~
服务员类:
~~~
using Cooks;
namespace Servers
{
/// <summary>
/// 服务员类
/// </summary>
public class Servers
{
string name;
Cooks.Cooks cook=new Cooks.Cooks("烤肉老王");
public Servers(string name)
{
this.name = name;
}
/// <summary>
/// 烤羊肉
/// </summary>
public void KaoYangRou()
{
Console.WriteLine(name + ":烤羊肉一串");
cook.BakeMutton();
}
/// <summary>
/// 烤鸡翅
/// </summary>
public void KaoJiChi()
{
Console.WriteLine(name + ":烤鸡翅一只");
cook.BakeChickenWing();
}
}
}
~~~
烤肉者类:
~~~
namespace Cooks
{
public class Cooks
{
string name;
public Cooks(string name)
{
this.name = name;
}
/// <summary>
/// 烤羊肉
/// </summary>
public void BakeMutton()
{
Console.WriteLine(name +":烤羊肉");
}
/// <summary>
/// 烤鸡翅
/// </summary>
public void BakeChickenWing()
{
Console.WriteLine(name + ":烤鸡翅");
}
}
}
~~~
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cf2178e3.png)
“勉勉强强吧,这样顾客在变化中,服务员在变化,就连烤肉师傅也可以换,这些变化都不会影响整个业务流程。每层的变化都是独立的,不会对其他的有影响。这就是人们常说的“解耦”。”小C总是高深莫测的样子。扶了扶眼睛,继续说道,
“其实利用分层,不光是解耦,还是可以快速开发。一个项目,分好多人,有团队的每个人去并发的开发一层,可以节约大量的时间。当然复用也是很有帮助的。”
“哈,这次吃烤肉没白吃啊,居然又邂逅三层了,又练习了对三层的应用,嗯,收获不小 ,希望明天考试也这样顺利就好了”小B发着感慨。
“不早了,都睡觉了,明天还要考试呢”小C提醒道。
“等会等会,我还没有看完电视呢!”中途跑去看电视的小A嚷嚷着。
“现在网络多发达啊,电视剧都放到网络上了,只要你在任何时间,任何地点,任何一台可以联网的机子,你就可以看想看的电视剧了,完全不受约束。”小B显摆的说道。突然眼睛一亮,
“又是三层!!”小B和小C同时喊道。然后相互望了一眼,哈哈大笑起来。
“神经,睡觉了!”小A受不了他们,躺下进入 了梦乡。。。。
再谈三层架构
最后更新于:2022-04-01 07:02:41
再次谈起三层架构来,初识三层时的那种向往,那种青涩,已经不见了踪影,取而代之的是对分层的感慨。
分层,三层也好,七层也罢,都是将页面显示、业务逻辑控制、数据访问进行解耦。还有MVC和设计模式也是这样。只分UI,BLL,DAL这三层,只是实现了基本的解耦,但是耦合性还是很高的,尤其是对于中型及以上的系统来说,简单的三层并不能满足其需求。DAL提取出DBHelper,BLL中提取出Facade层,还有各层之间其实都应该加上接口。这样系统的灵活性才会大大提高。
对于B层的划分,有人说按数据表走,一个表一个类;有人说按窗体走,一个窗体一个类(PS:我这里是重构,有原系统做参考,如果是开发一个新系统,则没有这种说法);有人说按用例走,一个用例一个类。合作开发完以后,我好好分析了一下这三者。第一种方法,B层的类会相对少一些,维护工作会比较轻松,不过假如增加一个功能,那么就得相应的修改B层的类,不满足开闭原则。第二种方法,局限性更大。只要修改窗体,就得修改类。这样给人的感觉,B层依赖于UI层。但是分层应该是上层依赖于下层。第三种方法,满足开闭原则,添加功能,直接添加一个类即可。但是不足的是B层中类太多了。分析之后。我觉得应该改善第三种方法,在B层包中,为每个表添加一个子包,然后对应B层的操作放到对应子包中。这样就不会太乱了。添加功能时,直接在对应的子包中添加类。一个用例一个类。满足了开闭原则和单一职责原则。
对于Facade层如何划分,暂时没有确定的想法。有2种观点:一是将有关系的类,相近的类放到一个外观类。另一种观点按UI层来划分。不过感觉第一种观点稍微科学一点。暂时没有更好的想法。
在个人版和合作版开发完后,感觉接口挺重要的,开发接口可以大大增加系统的灵活性。而且,每一层只要针对于接口进行编程即可。不用考虑下一层的具体实现。下一层如何更改都无关紧要,只要接口不变,那么系统运行无阻碍。在开发前期,先开发接口,这样上层接口在编程时,调用下层会很容易,且不易出错。调试的时候也很方便。
初识MVC与三层架构
最后更新于:2022-04-01 07:02:39
MVC 即 Model View Controller 。mvc是一种很好的设计模式的综合应用。其中
M 即Model(模型层),主要负责处理业务逻辑以及与数据库的交互。
V 即View(视图层),主要用于显示数据和提交数据。
C 即Controller(控制器),主要用于接受请求并控制请求转发。
三层架构,是最基本的项目分层结果。秉承“高内聚,低耦合”的思想,将整个业务应用划分为:UI层(表示层)、BLL(业务逻辑层)、DAL层(数据访问层)。系统的主要功能和业务逻辑在BLL层进行处理。
UI:表示层,也叫做用户界面层,用来接受用户的请求,以及数据返回。。可以是WEB、也可以是WinForm。
BLL:业务逻辑层,主要负责对数据层的操作,对数据业务逻辑的处理。
DAL:数据访问层,直接操作数据库,包括增、删、改、查、备份、还原等操作。
从二者的区别,我没有在网上找到一个定论,主要观点以下有几个:
① MVC是一种复合设计模式,而三层是一个软件架构。
软件架构是用接口来实现的,而设计模式则是一种设计思想、设计理念的升华,也可以算做一种解决方案。一个系统一般只有一个软件架构,而可以有多个设计模式,因为设计模式是可以复用的。
② 在三层架构中没有定义Controller的概念,而MVC中也没有将业务的逻辑访问看成两个层。当然了。在三层中也提到了Model,但是三层架构中Model(Entry)的概念与MVC中Model的概念是不一样的,“三层”中典型的Model层是以实体类构成的,而MVC里,则是由业务逻辑与访问数据组成的。
③三层结构模式又可归于部署模式,MVC可归于表示模式。
④三层架构划分为:View(UI)、BIZ(BLL)、ADO(DAL)、Entity(Model)、Controller。而MVC把 BIZ(BLL)、ADO(DAL)、Model(Entity) 统一称之为 模型(MODEL),得到:View、Controller、模型(MODEL) 三层。
我认为,二者并不是互斥的,相反,MVC和多层应用软件结构有着异曲同工之妙,而且可以相互结合进行开发。MVC中的每一个环节都可以采用多层应用软件结构进行设计。而多层应用软件中的每一层又可以采取MVC来进行设计。所以说可以是:M层对应于BLL和DAL。V对应于UI,而C则对应于层与层之间的联系。也可以M对应于DAL,C对应于BLL,而V仍然对应于UI。
设计模式之行为型模式
最后更新于:2022-04-01 07:02:37
行为型模式描述类或对象如何交互及如何分配职责,它 主要涉及通过合理的处理方法,达到使系统升级性和维护性提高的目的。
行为模式
1.职责链模式 Chain of Responsibility
2.命令模式 Command
3.解释器模式 Interpreter
4.迭代器模式 Iterator
5.中介者模式 Mediator
6.备忘录模式 Memento
7.观察者模式 Observer
8.状态模式 State
9.策略模式 Strategy
10.模板方法模式 Template Method
11.访问者模式 Visitor
### 职责链模式 Chain of Responsibility
职责链模式使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
“击鼓传球”游戏就是职责链模式的一种应用,鼓起,球从人手中传递,鼓落,拿球的人要做某件事。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cf09513f.gif)
### 命令模式Command
命令模式将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;可以对请求排队或记录请求日志,以及支持可撤销的操作。
命令模式是对“行为的请求者”和“行为的实现者”进行了解耦。
Invoker对象可以在不同的时刻指定、排列、执行操作,支持取消、重做的操作,支持事务,记录操作的日志。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cf0a5027.gif)
### 解释器模式 Interpreter
解释器模式,给定一个语言,定义它的问法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
一个特定类型的复杂问题频繁出现,这时我们可以用解释器模式将负责对象表述为一个简单的对象,再进行处理。正则表达式就是一个非常好的例子。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cf0b984a.gif)
### 迭代器模式 Iterator
迭代器模式提供了一种方法顺序访问一个聚合对象中各个元素,而又不需要暴露该对象的内部表示。当我们需要对聚集有多种方式遍历时,可以考虑用迭代器模式。迭代器模式提供“开始、下一个、是否结束、当前哪一项…”等统一的接口。
迭代器模式分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露内部结构,又可以让外部代码透明底访问集合内部的数据。松散了耦合性,做到了信息隐蔽。
比如老师向班长要一个学生花名册,班长可以按学号进行排列,也可以按姓名首字母进行排列,只要包括了全部学生就行了。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cf0db7c5.gif)
### 中介者模式 Mediator
中介者模式用一个中介对象来封装一系列的对象交互。中介者是各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
中介者模式,将多个对象之间的多对多的关系转变为了一对一的关系。对象间的相互通信,都需要通过中介者对象来完成,一个对象的增加和移除,不影响其他对象,这样就降低了他们之间的耦合。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cf102c37.gif)
### 备忘录模式 Memeton
备忘录模式在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可以将该对象恢复到原先保存的状态了。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cf138f76.gif)
### 观察者模式 Observer
观察者模式定义了对象间的一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖它的对象都会得到通知,并被自动更新,
系统中有两个方面,其中一个方面依赖与另一个方面,我们把这两个方面抽象,是各自可以独立的变化和复用。
就像我们现在所用到的分层,不就是一层层的依赖么?还有系统组件升级,系统功能也跟着变化,这也属于观察者模式。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cf14f3ab.gif)
### 状态模式 State
状态模式允许一个对象在其内部状态改变时改变它的行为,让对象看起来似乎修改了它的类。
状态模式就是把系统的多个状态分割开来,分布到State的子类中,消除了庞大的分支语句,减少了耦合,同时也很容易增加新的状态的和转换。
这就相当于一个Schedule或School Timetable一样,时间的改变,我们的行程或课程也随之改变。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cf16523f.gif)
### 策略模式 Strategy
策略模式定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。本模式使得算法的变化不会影响到使用算法的客户。
策略模式将每一个算法封装到一个具有公共接口的独立类中,解除了客户与具体算法的直接耦合,是客户改变算法更为容易。
策略模式+简单工厂+反射+配置文件可以组成更为灵活的方式。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cf186bd5.gif)
### 模版方法模式 Template Method
模版方法模式定义一个操作的算法骨架,而将一些步骤延迟到子类中,模版方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
模版方法就是把不变的行为搬到了超类中,去除了子类中的重复代码。
多个客户去银行开户,大家都需要填写一个开户表单,表单的格式都是一样的,但是每个人填写的内容却是不同的,客户是需要填写不同的内容即可,而不是把整个表单抄一遍。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cf19d3d0.gif)
### 访问者模式 Visitor
访问者模式表示一个作用于某对象结构中的个各原色的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
访问者模式适用于数据结构相对稳定的系统。它是将数据结构和作用于结构上的操作分离开来,完成了解耦,可以是操作集合自由演化,但不适合更改数据结构。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cf1c5203.gif)
设计模式之结构型模式
最后更新于:2022-04-01 07:02:34
下面说一下结构型模式:结构型模式是描述如何将类对象结合在一起,形成一个更大的结构,结构模式描述两种不同的东西:类与类的实例。故可以分为类结构模式和对象结构模式。
在GoF设计模式中,结构型模式有:
1.适配器模式 Adapter
2.桥接模式 Bridge
3.组合模式 Composite
4.装饰模式 Decorator
5.外观模式 Facade
6.享元模式 Flyweight
7.代理模式 Proxy
具体来说:
### 适配器模式Adapter
适配器模式是将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
两个成熟的类需要通信,但是接口不同,由于开闭原则,我们不能去修改这两个类的接口,所以就需要一个适配器来完成衔接过程。
变压器就是很好的适配器模式的例子。用电设备所需要的电压是9V,但是电线上的电压却是220V的,我们不能去更改它们的电压输入或输出,所以我们用到了变压器。变压器是220V的输入,9V的输出。这样就可以将200V的电压变为9V的电压,用变压器将用电设备连接到了电线上工作了。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cefc996d.gif)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cefdf53d.gif)
上面两幅图中,都是Client端需要Request这个方法,但是Adaptee没有这个方法,所以就需要提供一个中间件/包装类(Wrapper)Adapter类来衔接。不同的是第一幅图Adapter继承自Adaptee,而第二幅图是在Adapter类中包装了一个Adaptee的实例。这就决定了第一幅图讲的是“类的结构模式”,而第二幅图则是“对象的结构模式”。
### 桥接模式 Bridge
桥接模式将抽象部分与它的实现部分分离,是它们都可以独立地变化。它很好的支持了开闭原则和组合锯和复用原则。实现系统可能有多角度分类,每一种分类都有可能变化,那么就把这些多角度分离出来让他们独立变化,减少他们之间的耦合。
2个相互耦合的系列,每个系列都有各自的产品变动。将这2个系列抽象成2个角色类,将各自的变化封装到对象的角色类中,然后再将2个角色类之间用组合的关系表示,这样就大大简化了使用类继承的复杂性,逻辑变得清晰了,易于扩展和维护。
桥接模式封装了变化,完成了解耦,实现了弱耦合。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cf0076c7.gif)
### 组合模式 Composite
组合模式将对象组合成树形结构以表示部分-整体的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。就像“放羊娃的故事”一样(放羊—赚钱—娶妻---生娃—放羊…)。
廊坊师范学院=14个二级学院+院长、副院长;每一个二级学院=多个专业+二级学院院长、副院长;每一个专业有学生+班委。大学院可以增删二级学院和副院长,二级学院的结构跟大学院几乎一样,所以也可以增删专业和副院长。虽然结构复杂,但是操作简单,只需要进行简单的递归调用即可。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cf01a4d9.gif)
### 装饰模式 Decorator
装饰模式动态地给一个对象添加一些额外的职责,就增加功能来说,它比生成子类更灵活。也可以这样说,装饰模式把复杂类中的核心职责和装饰功能区分开了,这样既简化了复杂类,有去除了相关类中重复的装饰逻辑。装饰模式没有通过继承原有类来扩展功能,但却达到了一样的目的,而且比继承更加灵活,所以可以说装饰模式是继承关系的一种替代方案。
孙悟空有72变,每一变都可以给他带来一个附加功能,变成鱼可以在水下呼吸,变成鸟可以在天上飞翔。这就是典型的装饰模式。
装饰模式解耦了核心和装饰功能,锁业也是强调了松耦合。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cf037df6.gif)
### 外观模式 Facade
外观模式为子系统中的一组接口提供了同意的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
外观模式中,客户对各个具体的子系统是不了解的,所以对这些子系统进行了封装,对外只提供了用户所明白的单一而简单的接口,用户直接使用这个接口就可以完成操作,而不用去理睬具体的过程,而且子系统的变化不会影响到用户,这样就做到了信息隐蔽。
这就相当于新生接待员。新生对入学流程不清楚,但是接待员学长可是明白的。学生跟着接待员就可以把各个手续办理完毕了。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cf051a9c.gif)
### 享元模式 Flyweight
享元模式为运用共享技术有效的支持大量细粒度的对象。因为它可以通过共享大幅度地减少单个实例的数目,避免了大量非常相似类的开销。.
享元模式是一个类别的多个对象共享这个类别的一个对象,而不是各自再实例化各自的对象。这样就达到了节省内存的目的。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cf070b5a.gif)
### 代理模式 Proxy
为其他对象提供一种代理,并由代理对象控制对原对象的引用,以间接控制对原对象的访问。
我们想上外国的网站,但是国家给限制了,所以我们就需要一个可以登录到外国网站的ip,通过连接到这个ip,再通过这个ip去访问外国网站。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cf082871.gif)
### 代理模式与外观模式的区别
代理的客户对象无法直接访问目标对象,代理对象提供对单独目标对象的访问控制,而外观模式的客户对象可以直接访问子系统中的各个对象,但通常由外观对象提供对子系统个元件功能的简化的共同层次的调用接口。
### 代理模式与适配器的区别
二者都属于一种衔接性质的功能。代理对象和被代理对象的接口是同一个,但是客户没法直接访问被代理者,只能通过代理对象去完成被代理对象的访问。而适配器模式是将多个子系统封装起来,提供一个统一的外部接口,客户只需要使用这个外部接口即可访问对象的子系统了。
### 外观跟适配器的区别
二者都是对显存系统的封装。外观模式定义了一个新的接口,而适配器则是复用了一个原有的接口;适配器是用来适配对象的,而外观则是用来适配整个子系统的。
设计模式之创建型模式
最后更新于:2022-04-01 07:02:32
随着对软件工程研究的不断深入,设计模式也越来越多了,根据其目的准则,划分如下:
1. 创建型:creational 与对象的创建有关。
2. 结构型:Structural 处理类或对象之间的组合。
3. 行为型:behavioral 描述类或对象如何交互及如何分配职责。
创建型模式抽象了实例化的过程,将关于该系统使用的具体的类的信息封装了起来。允许客户用结构和功能差别很大的产品对象配置一个系统。配置可以是静态的,也可以是动态的。
通常创建对象的设计都是从工厂方法开始,当设计者发现需要更大的灵活性时,设计变回想其他创建型模式进行演化。
其中,创建型模式
1.抽象工厂模式 AbstractFactory
2.建造者模式 Builder
3.工厂方法模式 Factory Method
4.原型模式 Prototype
5.单例模式 Singleton
具体来说:
### 抽象工厂模式
抽象工厂模式提供一个创建一系列或相关依赖对象的接口,而无需指定他们具体的类。抽象工厂是工厂方法模型的抽象,每一个具体工厂创建多个产品类的对象。具体工厂类与产品类是一对多的关系。所以具体工厂之间是采用了不同的方法,创建了相同的对象。
在“增加产品族”上,抽象工厂很好地支持了“开放-封闭原则”,但是在“增加新产品的等级结构”上,需要更改所有的工厂类。这有违背了“开放-封闭原则”。所以综合来说,抽象工厂模型以一种倾斜的方式支持增加新的产品,它为新产品族的增加提供了方便,但不能为新产品的等级结构的增加提供同样的方便。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cef5850d.gif)
### 建造者模式
建造者模式是将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。其中Builder定义了构建对象所需的“部件”或者说“步骤”,而ConcreteBuilder提供了几种样本的构建“模版”,Director是决定选择哪个“模版”来构建产品。这就决定了建造者模型可以担当构建复杂对象的重任,这些对象内部构建间的建造顺序通常是稳定的,但对象内部的构建通常面临着复杂的变化。
由于构建者隐藏了产品的构建细节,所以若要改变一个产品的内部表示,只需要再定一个具体的建造者就可以了。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cef70c1a.gif)
### 工厂方法模式
工厂方法模型定义了一个用于创建对象的接口,让子类决定实例化哪一个类,工厂模式使一个类的实例化延迟到了其子类中。工厂方法模式是优化的简单工厂模式,它很好的支持了“开闭原则”。每一个具体的工厂只能构建一个类的对象。具体工厂类与产品类是一对一的关系。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cef8238f.gif)
### 原型模式
原型模式是用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象,而无需知道具体的创建细节。
建立相应数目的原型并克隆他们通常比每次用合适的状态手工实例化该类更方便一些。
.NET在System命名空间中提供了Icloneable接口,可以通过该接口的Clone()方法来完成原型模式。在使用原型模式中应该注释浅拷贝和深拷贝(针对与引用类型)。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cef9fe0d.gif)
### 单例模式
单例模式保证一个类仅有一个实例,并提供一个访问他的全局访问点。单例模式必须自己创建自己的唯一实例。当一个系统要求某个类只要一个实例化对象,即可使用单例模式。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cefb3984.gif)
设计模式之设计原则
最后更新于:2022-04-01 07:02:30
设计模式(Design Pattern)是面向对象技术的最新进展之一,由于面向对象设计的灵活性,增加了其设计的复杂性,设计模式的出现就是为了提高复用的设计方案,让代码更容易被他人理解、保证代码可靠性。设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。
要想用好设计模式,必须先明白设计模式的六大原则:单一职责原则、开放封闭原则、依赖倒转原则、里氏代换原则、合成聚合复用原则、迪米特法则。
### ① 【单一职责原则SRP】
单一职责原则,就一个类而言,应该仅有一个引起它变化的原因。如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会消弱或者一直这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意想不到的破坏。而软件设计真正要做的许多内容,就是发现职责,并把这些职责相互分离。
### ② 【开放封闭原则OCP】
开放-封闭原则,是说软件实体(类、模块、函数等等)应该可以扩展,但是不可以修改。即对于扩展是开放的,对于更改是封闭的。当用户需求的变化,不鞥轻易更改源代码,而是应该创建抽象类,去隔离以后发生的同类变化。这样会使得变化中的系统有一定的稳定性和延续性。
例如当年玉皇大帝在不更改现有天庭秩序的情况下,成功扩展“弼马温”这个秩序(类)。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970ceef39b4.gif)
### ③ 【依赖倒转原则DIP】
抽象不应该依赖细节,细节应该依赖于抽象。即我们要针对接口(抽象类)编程,不要针对与实现编程。
比如你开车进小区,小王是门卫,每次你到门口,喊一声“小王,帮忙开下门”他就给你开了,但是对于刚搬进小区的小李,他可不认识小王,他怎么通知小王开门呢?假如突然有一天,门卫换了,难道你就不进小区了?答案大家都知道,只要喊“门卫,帮忙开下门”,你和小李就都可以进去了。所以不管门卫和小区的业主怎么换,只要“门卫”这个“接口”是不变,那么业主就可以让门卫给开门。
### ④ 【里氏代换原则LSP】
里氏代换原则,子类型必须能够替换掉他们的父类型。在软件里面,把父类都替换成其子类,程序的行为不会发生变化。
简单来说,子类=父类 [+新功能]。父类的非私有成员都会被子类继承,还可以根据自身情况对继承来的方法DIY。当用子类去实例化父类,调用父类的方法,具体实现则是刚才的子类的方法。(也就是说如果这个父类有多个子类的话,只要用不同的子类去实例化这个父类,那么这个父类对象执行相同的方法,则会执行各自子类对应的同名方法,所以结果也会不同。而这就是“多态”!所以可以说多态是里氏代换原则的体现。)当然反过来是不成立的。例如矩形和正方形,我们可以说正方形是一种特殊的矩形,但不可以说矩形是一种特殊的正方形。
### ⑤ 【合成聚合复用原则CARP】
合成聚合复用原则:尽量使用合成\聚合,尽量不使用类继承。合成聚合是”has a”的关系,而继承是“is a”的关系。由于继承是一中强耦合的结构,父类变,子类必变。所以不是“is a”关系,我们一般不要用继承。优先使用合成聚合复用原则,可以保持每个类的封装,降低继承的层次。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cef142dc.gif)
根据此原则转变后为:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56970cef38cac.gif)
### ⑥ 【迪米特法则】
如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法时,可以通过第三者转发这个调用。类之间的耦合越弱,就越有利于复用,一个处在弱耦合的类被修改,不会对有关系的类造成波及。也就是说,一个对象对其他对象尽可能少的了解(不要和陌生人说话)。
前言
最后更新于:2022-04-01 07:02:27
> 原文出处:[设计模式与系统架构](http://blog.csdn.net/column/details/my-designpattern.html)
作者:[崔成龙](http://blog.csdn.net/xiaoxian8023)
**本系列文章经作者授权在看云整理发布,未经作者允许,请勿转载!**
# 设计模式与系统架构
> 设计模式与系统架构,高级程序员的生存之本啊。自己向这方面正在迈进,路途中记录着自己对与这两块知识的认识和收获。