设计模式基础
一、设计模式简介
设计模式是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。
使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。
总结:简单地说设计模式就是一些常用编码规范的总结,是一种思想,如果你是大牛,也可以自己写一种设计模式供大家使用
二、设计模式四大要素
模式名称 :以一个恰当的词来描述模式的问题、解决方案和效果。
问题 :描述应该在何时使用设计模式。
解决方案 :提供设计问题的抽象描述和怎样用一个具有一般意义的元素组合(类或对象)来解决这个问题。
效果:描述设计模式应用的效果及使用设计模式应该权衡的问题。
三、设计模式六大原则(武功秘籍,懂了秘籍自己就可以根据秘籍去创造自己的设计模式,不必拘泥于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 }