参考:https://www.runoob.com/design-pattern/design-pattern-intro.html
目录
一、前言
最近重看设计模式感触良多,做软件还是需要多看设计模式的,好处多多,这个轻松学习设计模式的第二篇。
二、设计模式六大原则
这几个原则还是需要重复的。
1.开闭原则(Open Close Principle)
开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类。
2.里氏代换原则(Liskov Substitution Principle)
里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
3.依赖倒转原则(Dependence Inversion Principle)
这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。
4.接口隔离原则(Interface Segregation Principle)
这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。
5.迪米特法则(最少知道原则)(Demeter Principle)
为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。
6.合成复用原则(Composite Reuse Principle)
原则是尽量使用合成/聚合的方式,而不是使用继承。
三、模式分类
创建型:单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式;
结构型:代理模式、适配器模式、装饰器模式、桥接模式、组合模式、享元模式、外观模式;
行为型:观察者模式、模板方法模式、命令模式、状态模式、职责链模式、解释器模式 、中介者模式、访问者模式、策略模式、备忘录模式、迭代器模式;
J2EE 模式:MVC 模式(MVC Pattern)、业务代表模式(Business Delegate Pattern)、组合实体模式(Composite Entity Pattern)、数据访问对象模式(Data Access Object Pattern)、前端控制器模式(Front Controller Pattern)、拦截过滤器模式(Intercepting Filter Pattern)、服务定位器模式(Service Locator Pattern)、传输对象模式(Transfer Object Pattern)。
四、结构型模式解析
1、代理模式
在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。
class Image
{
vitrual void display()=0;
}
class RealImage :public Image
{
public:
RealImage(String fileName){
this.fileName = fileName;
loadFromDisk(fileName);
}
public:
virtual void display()
{
System.out.println("Displaying " + fileName);
}
private :
void loadFromDisk(String fileName)
{
System.out.println("Loading " + fileName);
}
private:
String fileName;
}
class ProxyImage :public Image
{
private:
RealImage realImage;
String fileName;
public:
ProxyImage(String fileName)
{
this.fileName = fileName;
}
virtual void display()
{
if(realImage == null){
realImage = new RealImage(fileName);
}
realImage.display();
}
}
从代理模式的实现代码我们可以发现,类RealImage和类ProxyImage都实现了基类的display的接口,ProxyImage中实现是通过使用RealImage对象来实现display接口的。
int main(String[] args)
{
Image image = new ProxyImage("test_10mb.jpg");
// 图像将从磁盘加载
image.display();
System.out.println("");
// 图像不需要从磁盘加载
image.display();
}
从代理模式的调用代码我们可以看出,我们只需要使用代理类,不需要和未代理类打交道。
2、适配器模式
class MediaPlayer
{
public:
vitrual void play(String audioType, String fileName)=0;
}
class MediaAdapter : public MediaPlayer
{
AdvancedMediaPlayer advancedMusicPlayer;
public:
MediaAdapter(String audioType)
{
if(audioType.equalsIgnoreCase("vlc") ){
advancedMusicPlayer = new VlcPlayer();
} else if (audioType.equalsIgnoreCase("mp4")){
advancedMusicPlayer = new Mp4Player();
}
}
vitrual void play(String audioType, String fileName)
{
if(audioType.equalsIgnoreCase("vlc")){
advancedMusicPlayer.playVlc(fileName);
}else if(audioType.equalsIgnoreCase("mp4")){
advancedMusicPlayer.playMp4(fileName);
}
}
}
class AudioPlayer :public MediaPlayer {
MediaAdapter mediaAdapter;
public:
vitual void play(String audioType, String fileName)
{
//播放 mp3 音乐文件的内置支持
if(audioType.equalsIgnoreCase("mp3")){
System.out.println("Playing mp3 file. Name: "+ fileName);
}
//mediaAdapter 提供了播放其他文件格式的支持
else if(audioType.equalsIgnoreCase("vlc")
|| audioType.equalsIgnoreCase("mp4")){
mediaAdapter = new MediaAdapter(audioType);
mediaAdapter.play(audioType, fileName);
}
else{
System.out.println("Invalid media. "+
audioType + " format not supported");
}
}
}
从实现模式的代码中我们可以发现,类MediaAdapter 对类AdvancedMediaPlayer 实现了适配,类AudioPlayer 又适配了类MediaAdapter,AudioPlayer 从而实现进行播放不同的格式。
int main()
{
AudioPlayer audioPlayer = new AudioPlayer();
audioPlayer.play("mp3", "beyond the horizon.mp3");
audioPlayer.play("mp4", "alone.mp4");
audioPlayer.play("vlc", "far far away.vlc");
audioPlayer.play("avi", "mind me.avi");
}
从调用代码我们可以发现,我们只需要和audioPlayer打交道就可以实现不同格式音乐的播放。从而统一了对外调用。
3、装饰器模式
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
3.1、UML图
3.2 模式设计代码解析
class Shape
{
virtual void draw()=0;
}
class Rectangle :Public Shape {
public :
vitural void draw() {
System.out.println("Shape: Rectangle");
}
}
class Circle :public Shape
{
public:
virtual void draw() {
System.out.println("Shape: Circle");
}
}
class ShapeDecorator: public Shape
{
public:
ShapeDecorator(Shape decoratedShape){
this.decoratedShape = decoratedShape;
}
virtual void draw(){
decoratedShape.draw();
}
protected:
Shape decoratedShape;
}
public class RedShapeDecorator :public ShapeDecorator
{
public:
virtual void draw() {
decoratedShape.draw();
setRedBorder(decoratedShape);
}
private:
void setRedBorder(Shape decoratedShape){
System.out.println("Border Color: Red");
}
}
public class TxtShapeDecorator :public ShapeDecorator
{
public:
virtual void draw() {
decoratedShape.draw();
setText(decoratedShape);
}
private:
void setText(Shape decoratedShape){
System.out.println("Border Color: Red");
}
}
模式设计代码还是比较清晰易懂的,ShapeDecorator是装饰类的抽象基类,它也是集成自shape类的,这样我们就可以设计许多个装饰类从而实现对对象的迭代装饰,就如同一件一件穿衣服一样。
3.3 模式调用代码解析
int main()
{
Shape circle = new Circle();
ShapeDecorator redCircle = new RedShapeDecorator(new Circle());
ShapeDecorator redCircleText = new TxtShapeDecorator(redCircle);
System.out.println("Circle with normal border");
circle.draw();
System.out.println("\nCircle of red border");
redCircle.draw();
System.out.println("\nCircle of red border and text");
redCircleText .draw();
}
调用代码也是比较清晰的,我们首先定义了一个circle对象,然后使用RedShapeDecorator和TxtShapeDecorator类对其进行装饰,可以得到一个有边框和文本的圆。
4、桥接模式
桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。这种模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类。这两种类型的类可被结构化改变而互不影响。
4.1 UML图
4.2 模式实现
class DrawAPI
{
public:
virtual void drawCircle(int radius, int x, int y)=0;
}
class RedCircle :public DrawAPI
{
public:
void drawCircle(int radius, int x, int y) {
System.out.println("Drawing Circle[ color: red, radius: "
+ radius +", x: " +x+", "+ y +"]");
}
}
class GreenCircle :public DrawAPI
{
public:
virutal void drawCircle(int radius, int x, int y) {
System.out.println("Drawing Circle[ color: green, radius: "
+ radius +", x: " +x+", "+ y +"]");
}
}
class Shape
{
protected:
Shape(DrawAPI drawAPI){
this.drawAPI = drawAPI;
}
public:
virtual void draw()=0;
protected:
DrawAPI drawAPI;
}
class Circle :public Shape
{
public:
Circle(int x, int y, int radius, DrawAPI drawAPI) {
super(drawAPI);
this.x = x;
this.y = y;
this.radius = radius;
}
virtual void draw() {
drawAPI.drawCircle(radius,x,y);
}
private:
int x, y, radius;
}
模式实现代码不复杂,上面的代码实现了shape接口和shape绘制实现之间的分离 ,我们可以使用RedCircle和GreenCircle去进行Circle的绘制,灵活性强。
4.3 模式调用
int main()
{
Shape redCircle = new Circle(100,100, 10, new RedCircle());
Shape greenCircle = new Circle(100,100, 10, new GreenCircle());
redCircle.draw();
greenCircle.draw();
}
调用这里我们需要何Shape类、Circle类、RedCircle类和GreenCircle类打交道,桥接接口将接口和实现相分离带来的好处是灵活性强,不利的就是调用方需要打交道的类比较多。
5、总结
今天分析了几个结构性的设计模式,我们应该根据我们的使用场景灵活选择。结构型设计模式还没介绍完,下次继续。今天就先写到这吧,设计模式估计要写一段时间才能写完了。