首页 > 技术文章 > 设计模式基础

leeSmall 2018-03-21 22:45 原文

一、设计模式简介

设计模式是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。

使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。

总结:简单地说设计模式就是一些常用编码规范的总结,是一种思想,如果你是大牛,也可以自己写一种设计模式供大家使用

二、设计模式四大要素

模式名称 :以一个恰当的词来描述模式的问题、解决方案和效果。

问题 :描述应该在何时使用设计模式。

解决方案 :提供设计问题的抽象描述和怎样用一个具有一般意义的元素组合(类或对象)来解决这个问题。

效果:描述设计模式应用的效果及使用设计模式应该权衡的问题。

三、设计模式六大原则(武功秘籍,懂了秘籍自己就可以根据秘籍去创造自己的设计模式,不必拘泥于23中设计模式)

1. 开闭原则

开放封闭有两个含义,一个是对于拓展是开放的,另一个是对于修改是关闭的。在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误,也可能会使我们不得不对整个功能进行重构,并且需要原有代码经过重新测试。

解决方案: 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。

总结:

面向对象设计/编程 终极目标(实现开闭原则)
一个是对于拓展是开放的,另一个是对于修改是关闭的
尽量不要修改已有代码

2. 单一职责原则

通俗讲就是我们不要让一个类承担过多的职责。如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到破坏。

解决方案: 分别建立两个类C1、C2,使类C1完成职责A,C2完成职责B。这样,当修改类C1时,不会使职责B发生故障风险;同理,当修改类C2时,也不会使职责A发生故障风险。

总结:

不要让一个类承担过多的职责

3. 里氏替换原则

里氏代换原则告诉我们,在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常。

解决方案: 当使用继承时,遵循里氏替换原则。类B继承类A时,除添加新的方法完成新增功能外,尽量不要重写父类A的方法,也尽量不要重载父类A的方法。

总结:

继承父类后,尽量不要重写或者重载父类的方法。

4. 依赖倒转原则

高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。 类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。

解决方案: 将类A修改为依赖接口I,类B和类C各自实现接口I,类A通过接口I间接与类B或者类C发生联系,则会大大降低修改类A的几率。

总结:

通过抽象或者接口来实现类与类之间的依赖关系。

5. 接口隔离原则

建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。也就是说,我们要为各个类建立专用的接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。

解决方案: 将臃肿的接口拆分为独立的几个接口,实现类分别与他们需要的接口建立依赖关系。

总结:

建立单一接口,不要建立庞大/臃肿的接口

6. 迪米特法则

迪米特法则也称为最少知道原则,一个实体应该尽可能少的与另外一个实体发生相互作用(依赖关系)。 

解决方案: 尽可能降低类之间的耦合

总结:

只提供方法给外部调用,不要让外部知道你是怎么做的

四、设计模式分类

创建型设计模式:

创建型模式抽象实例化过程,在什么对象被创建、谁创建它、它是怎么被创建的以及何时创建这些方面给予你很大的灵活性——主要处理对象的创建,实例化对象

结构型设计模式:

结构型模式涉及到如何组合类和对象以获得更大的结构,采用继承机制来组合接口或实现——处理类或对象的组合以获得更庞大的功能

行为型设计模式:

行为模式涉及到算法和对象间职责的分配,不仅描述对象或类的模式,还描述它们之间的通信模式——处理对象之间的交互

五、常用的六种设计模式(招式,设计模式六大原则的实现)

1. 创建型模式之【单例模式】

问题 对于某些场景,确保一个类只有一个实例对象是很重要的,并且这个实例是易于被访问的。

解决方案:将实例化操作隐藏在一个类中,由这个类来保证实例的创建和唯一性。

单例模式 饿汉(一开始就创建实例)

 1 package com.study.demo1;
 2 /**
 3  * 
 4  * @author lgs
 5  * @redame 单例模式 饿汉(一开始就创建实例)
 6  */
 7 public class SingletonObjectA {
 8     private static SingletonObjectA instance = new SingletonObjectA();
 9     
10     private SingletonObjectA(){}
11     
12     public static SingletonObjectA getInstance(){
13         return instance;
14     }
15 }

单例模式 饱汉(需要的时候才创建实例)

 1 package com.study.demo1;
 2 /**
 3  * 
 4  * @author lgs
 5  * @redame 单例模式 饱汉(需要的时候才创建实例)
 6  */
 7 public class SingletonObjectB {
 8     private static volatile SingletonObjectB instance = null;
 9     
10     private SingletonObjectB(){}
11     
12     public static SingletonObjectB getInstance(){
13         if(instance==null){
14             synchronized(instance){
15                 if(instance == null){
16                     instance = new SingletonObjectB();
17                 }
18             }
19         }
20         return instance;
21     }
22 }

主程序入口:

 1 public class Demo1 {
 2     public static void main(String[] args) {
 3         SingletonObjectA object1 = SingletonObjectA.getInstance();
 4         
 5         SingletonObjectA object2 = SingletonObjectA.getInstance();
 6         
 7         System.out.println(object1);
 8         System.out.println(object2);
 9     }
10 }

2. 创建型模式之【工厂模式】

问题:当一个类无法确定要创建哪种类的对象,或者需要通过子类来确定具体对象的创建时。

解决方案:创建一个创建对象的工厂,工厂在程序执行过程中具体决定创建哪个对象。

发送消息的接口:

 1 package com.study.demo2;
 2 /**
 3  * 
 4  * @author lgs
 5  * 
 6  */
 7 public interface Sender {
 8 
 9     public void send();
10 }

使用邮件发送消息:

 1 package com.study.demo2;
 2 /**
 3  * 
 4  * @author lgs
 5  * 
 6  */
 7 public class MailSender implements Sender {
 8 
 9     @Override
10     public void send() {
11         System.out.println("Use the email sender message.");
12     }
13 
14 }

使用SMS发送消息:

 1 package com.study.demo2;
 2 /**
 3  * 
 4  * @author lgs
 5  * 
 6  */
 7 public class SmsSender implements Sender {
 8 
 9     @Override
10     public void send() {
11         System.out.println("Use the SMS sender messages.");
12     }
13 
14 }

使用微信发送消息:

 1 package com.study.demo2;
 2 /**
 3  * 
 4  * @author lgs
 5  * 
 6  */
 7 public class WechatSender implements Sender {
 8 
 9     @Override
10     public void send() {
11         System.out.println("Use the Wechat sender messages.");
12     }
13 
14 }

普通工厂模式:

 1 package com.study.demo2;
 2 /**
 3  * 
 4  * @author lgs
 5  * @redame 普通工厂模式
 6  */
 7 public class MessageFactoryA {
 8     public static String MAIL="MAIL";
 9     public static String SMS="SMS";
10     public static String WE_CHAT="WE_CHAT";
11     
12     public Sender product(String type){
13         if(MessageFactoryA.MAIL.equals(type)){
14             return new MailSender();
15         } else if(MessageFactoryA.SMS.equals(type)){
16             return new SmsSender();
17         } else if(MessageFactoryA.WE_CHAT.equals(type)){
18             return new WechatSender();
19         }
20         return null;
21     }
22 }

工厂方法模式:

 1 package com.study.demo2;
 2 /**
 3  * 
 4  * @author lgs
 5  * @redame 工厂方法模式
 6  */
 7 public class MessageFactoryB {
 8 
 9     public Sender productMail(){
10         return new MailSender();
11     }
12     public Sender productSMS(){
13         return new SmsSender();
14     }
15     public Sender productWechat(){
16         return new WechatSender();
17     }
18 }

主程序入口:

 1 package com.study.demo2;
 2 
 3 /**
 4  * 
 5  * @author lgs
 6  */
 7 public class Demo2 {
 8     public static void main(String[] args) {
 9         // 普通工厂模式
10         // MessageFactoryA factory = new MessageFactoryA();
11         // Sender sender = factory.product(MessageFactoryA.WE_CHAT);
12         // sender.send();
13         // 工厂方法模式
14         MessageFactoryB factory = new MessageFactoryB();
15         Sender sender = null;
16         if (MessageFactoryA.WE_CHAT.equals("1")) {
17             sender = factory.productWechat();
18         }
19 
20     }
21 }

3. 结构型模式之【适配器模式】

问题:

有时,为复用而设计的工具箱类不能够被复用的原因,仅仅是因为它的接口与专业应用领域所需要的接口不匹配。

解决方案:

应用通过适配器调用接口,由适配器使得工具箱类可以被使用。

总结:把不同的功能集成到适配器。类似通过转换器把香港iPhone的充电器插头和大陆的iPhone的充电器插头一起使用

3.1 类适配器模式:

 1 package com.study.demo3.classType;
 2 /**
 3  * 
 4  * @author lgs
 5  * 
 6  */
 7 public class Source {
 8 
 9     public void method1(){
10         System.out.println("一个普通方法实现.");
11     }
12 }
 1 package com.study.demo3.classType;
 2 /**
 3  * 
 4  * @author lgs
 5  * 
 6  */
 7 public interface Targetable {
 8 
 9     //与原类中的方法相同
10     public void method1();
11     
12     //新类中的方法
13     public void method2();
14 }

 

 1 package com.study.demo3.classType;
 2 /**
 3  * 
 4  * @author lgs
 5  * @类适配器模式:通过继承Source类,Targetable接口的实现类拥有了Source的方法.
 6  */
 7 public class SpecialAdapter extends Source implements Targetable {
 8 
 9     @Override
10     public void method2() {
11         System.out.println("这是一个特殊方法实现.");
12     }
13 
14 }

主程序入口:

1 public class Demo31 {
2 
3     public static void main(String[] args) {
4         Targetable target = new SpecialAdapter();
5         target.method1();
6         target.method2();
7     }
8 }

3.2 对象适配器模式:

 1 package com.study.demo3.objectType;
 2 /**
 3  * 
 4  * @author lgs
 5  * 
 6  */
 7 public class Source {
 8 
 9     public void method1(){
10         System.out.println("一个普通方法实现.");
11     }
12 }

 

 1 package com.study.demo3.objectType;
 2 /**
 3  * 
 4  * @author lgs
 5  * 
 6  */
 7 public interface Targetable {
 8 
 9     //与原类中的方法相同
10     public void method1();
11     
12     //新类中的方法
13     public void method2();
14 }

 

 1 package com.study.demo3.objectType;
 2 /**
 3  * 
 4  * @author lgs
 5  * @对象适配器模式
 6  */
 7 public class SpecialAdapter implements Targetable {
 8 
 9     /**
10      * 可匹配Source的子类 
11      **/
12     private Source source;
13     public SpecialAdapter(Source source){
14         super();
15         this.source = source;
16     }
17     @Override
18     public void method1() {
19         source.method1();
20     }
21 
22     @Override
23     public void method2() {
24         System.out.println("这是一个特殊方法实现.");
25     }
26 
27 }

 

主程序入口:

1 public class Demo32 {
2     public static void main(String[] args) {
3         
4         Targetable adapter = new SpecialAdapter(new Source());
5         adapter.method1();
6         adapter.method2();
7     }
8 }

 

4. 结构型模式之【代理模式】

问题:

对象的访问需要被控制,不允许其他对象任意访问此对象接口。

解决方案:

代理类开放接口提供访问,所有访问由代理类决定具体的调用。

总结:

类似于经纪人和艺人的关系,艺人的任何事情都要先经过经纪人的许可才能去做

 1 package com.study.demo4;
 2 /**
 3  * 
 4  * @author lgs
 5  * 
 6  */
 7 public abstract class AbstractObject {
 8 
 9     //代理方法
10     public abstract void operation();
11 }

 

 1 package com.study.demo4;
 2 /**
 3  * 
 4  * @author lgs
 5  * @真正做事的人
 6  * 
 7  */
 8 public class RealObject extends AbstractObject {
 9 
10     @Override
11     public void operation() {
12         System.out.println("具体执行任务.");
13     }
14 
15 }
 1 package com.study.demo4;
 2 /**
 3  * 
 4  * @author lgs
 5  * @代理人
 6  * 
 7  */
 8 public class ProxyObject extends AbstractObject {
 9 
10     RealObject realObject = new RealObject();
11     @Override
12     public void operation() {
13         System.out.println("执行任务前, 做一些什么事情.");
14         realObject.operation();
15         
16     }
17 
18 }

 

主程序入口:

1 public class Demo4 {
2     public static void main(String[] args) {
3         AbstractObject obj = new ProxyObject();
4         obj.operation();
5     }
6 }

5. 行为模式之【观察者模式】

问题:

将一个系统分割成一系列相互协作的类有一个常见的副作用:需要维护相关对象间的一致性,然而维护对象间的一致性可能导致各类之间的紧密耦合,这样将降低它们的可重用性。

解决方案:

观察者模式建立一个目标和任意个依赖它的观察者,一旦目标状态发生改变,所有的观察者都得到通知。

1 package com.study.demo5;
2 /**
3  * 
4  * @author lgs
5  * 
6  */
7 public interface Observer {
8     public void update();
9 }
 1 package com.study.demo5;
 2 /**
 3  * 
 4  * @author lgs
 5  * 
 6  */
 7 public class ObserverA implements Observer {
 8 
 9     @Override
10     public void update() {
11         System.out.println("观察者A观察到对象的变化.");
12     }
13 
14 }
 1 package com.study.demo5;
 2 /**
 3  * 
 4  * @author lgs
 5  * 
 6  */
 7 public class ObserverB implements Observer {
 8 
 9     @Override
10     public void update() {
11         System.out.println("观察者B观察到对象的变化.");
12     }
13 
14 }
 1 package com.study.demo5;
 2 /**
 3  * 
 4  * @author lgs
 5  * 
 6  */
 7 public interface Subject {
 8     //本身的任务调用
 9     public void operation();
10     
11 }
 1 package com.study.demo5;
 2 
 3 import java.util.Vector;
 4 
 5 /**
 6  * 
 7  * @author lgs
 8  * 
 9  */
10 public abstract class AbstractSubject implements Subject {
11 
12     private Vector<Observer> observerList = new Vector<Observer>();
13     
14     public void add(Observer observer) {
15         observerList.add(observer);
16     }
17 
18     public void del(Observer observer) {
19         observerList.remove(observer);
20     }
21 
22     public void notifyObservers() {
23         for (Observer observer : observerList) {
24             observer.update();
25         }
26     }
27 }

 

 1 package com.study.demo5;
 2 /**
 3  * 
 4  * @author lgs
 5  * 
 6  */
 7 public class MySubject extends AbstractSubject {
 8 
 9     @Override
10     public void operation() {
11         System.out.println("执行方法, 状态修改. ");
12         //通知所有观察者
13         notifyObservers();
14     }
15 
16 }

 

主程序入口:

1 public class Demo5 {
2     public static void main(String[] args) {
3         AbstractSubject subject = new MySubject();
4         subject.add(new ObserverA());
5         subject.add(new ObserverB());
6         subject.operation();
7     }
8 }

 

6. 行为模式之【策略模式】

问题:

开发中常见的情况:实现某功能可以有多种算法或者策略,可根据实际情况选择不同的算法或策略来完成该功能。如果将所有算法或策略都封装在一个类中,提供不同方法来实现,这个类就变得臃肿,而且新增算法或策略时,需要修改封装算法类的源码。

解决方案:

使用不同类来封装不同的算法

 1 package com.study.demo6;
 2 /**
 3  * 
 4  * @author lgs
 5  * 
 6  */
 7 public interface MemberStrategy {
 8     /**
 9      * 计算图书的价格
10      * @param booksPrice    图书的原价
11      * @return    计算出打折后的价格
12      */
13     public double calcPrice(double booksPrice);
14 }
 1 package com.study.demo6;
 2 /**
 3  * 
 4  * @author lgs 6  * 
 7  */
 8 public class AdvancedMemberStrategy implements MemberStrategy {
 9 
10     @Override
11     public double calcPrice(double booksPrice) {
12         System.out.println("高级会员8折");
13         return booksPrice * 0.8;
14     }
15 
16 }
 1 package com.study.demo6;
 2 /**
 3  * 
 4  * @author lgs 6  * 
 7  */
 8 public class IntermediateMemberStrategy implements MemberStrategy {
 9 
10     @Override
11     public double calcPrice(double booksPrice) {
12         System.out.println("中级会员9折");
13         return booksPrice * 0.9;
14     }
15 
16 }
 1 package com.study.demo6;
 2 /**
 3  * 
 4  * @author lgs 6  * 
 7  */
 8 public class PrimaryMemberStrategy implements MemberStrategy {
 9 
10     @Override
11     public double calcPrice(double booksPrice) {
12         System.out.println("初级会员没有折扣");
13         return booksPrice;
14     }
15 
16 }

 

 1 package com.study.demo6;
 2 /**
 3  * 
 4  * @author lgs
 5  * 
 6  */
 7 public class Price {
 8     //持有一个具体的策略对象
 9     private MemberStrategy strategy;
10 
11     /**
12      * @param strategy
13      */
14     public Price(MemberStrategy strategy) {
15         super();
16         this.strategy = strategy;
17     }
18     
19     /**
20      * 计算图书的价格
21      * @param booksPrice    图书的原价
22      * @return    计算出打折后的价格
23      */
24     public double quote(double booksPrice){
25         return this.strategy.calcPrice(booksPrice);
26     }
27 }

 

主程序入口:

 1 package com.study.demo6;
 2 /**
 3  * 
 4  * @author lgs
 5  * 
 6  */
 7 public class Demo6 {
 8     public static void main(String[] args) {
 9         //选择并创建需要使用的策略对象
10         MemberStrategy strategy = new AdvancedMemberStrategy();
11         //创建环境
12         Price price = new Price(strategy);
13         //计算价格
14         double quote = price.quote(300);
15         System.out.println("图书的最终价格为:" + quote);
16     }
17 }

 

推荐阅读