文章目录
一、简介
软件设计模式(Design pattern),又称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。
二、六大原则
1、开闭原则
开闭原则(Open Close Principle)的意思是:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。
2、里氏代换原则
里氏代换原则(Liskov Substitution Principle)是面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
3、依赖倒转原则
依赖倒转原则(Dependence Inversion Principle)是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。
4、接口隔离原则
接口隔离原则(Interface Segregation Principle)的意思是:使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。
5、最少知道原则
迪米特法则,又称最少知道原则(Demeter Principle)是指:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。
6、合成复用原则
合成复用原则(Composite Reuse Principle)是指:尽量使用合成/聚合的方式,而不是使用继承。
三、实例详解
总体来说设计模式分为三大类:
- 创建型模式(5种):工厂模式、抽象工厂模式、单例模式、构建者模式、原型模式。
- 结构型模式(7种):适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
- 行为型模式(11种):策略模式、模板模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
根据分类,我画了一份脑图,如下图:
1、工厂模式
定义一个用于创建产品的接口,由子类决定生产什么产品。典型的案例:JDBC连接数据库,通过加载不同的数据库驱动,得到不同的数据库连接。
Phone类:手机标准规范类
public interface Phone {
void make();
}
HuaWeiPhone类:制造华为手机
public class HuaWeiPhone implements Phone{
@Override
public void make() {
System.out.println("make huawei phone");
}
}
XiaoMiPhone类:制造小米手机
public class XiaoMiPhone implements Phone{
@Override
public void make() {
System.out.println("make xiaomi phone");
}
}
PhoneFactory类:手机代工厂(Factory)
public class PhoneFactory {
public Phone makePhone(String phoneType) {
if(phoneType.equalsIgnoreCase("xiaomi")){
return new XiaoMiPhone();
}
else if(phoneType.equalsIgnoreCase("huawei")) {
return new HuaWeiPhone();
}
return null;
}
}
演示:
public class Demo {
public static void main(String[] args) {
PhoneFactory phoneFactory = new PhoneFactory();
Phone huawei = phoneFactory.makePhone("huawei");
huawei.make();
Phone xiaomi = phoneFactory.makePhone("xiaomi");
xiaomi.make();
}
}
控制台输出:
make huawei phone
make xiaomi phone
2、抽象工厂模式
提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品。例如,创建小米工厂和华为工厂,生产多个产品(手机、电脑、电视…)。
Phone类:手机标准规范类
public interface Phone {
void make();
}
HuaWeiPhone类:制造华为手机
public class HuaWeiPhone implements Phone{
@Override
public void make() {
System.out.println("make huawei phone");
}
}
XiaoMiPhone类:制造小米手机
public class XiaoMiPhone implements Phone{
@Override
public void make() {
System.out.println("make xiaomi phone");
}
}
AbstractFactory类:抽象工厂类
public interface AbstractFactory {
Phone makePhone();
}
XiaoMiFactory类:制造小米产品的工厂
public class XiaoMiFactory implements AbstractFactory {
@Override
public Phone makePhone() {
return new XiaoMiPhone();
}
}
HuaWeiFactory类:制造华为产品的工厂
public class HuaWeiFactory implements AbstractFactory{
@Override
public Phone makePhone() {
return new HuaWeiPhone();
}
}
演示:
public class Demo {
public static void main(String[] args) {
HuaWeiFactory huaWeiFactory = new HuaWeiFactory();
Phone huawei = huaWeiFactory.makePhone();
huawei.make();
XiaoMiFactory xiaoMiFactory = new XiaoMiFactory();
Phone xiaomi = xiaoMiFactory.makePhone();
xiaomi.make();
}
}
控制台输出:
make huawei phone
make xiaomi phone
3、单例模式
某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。例如,一些设备管理器常常设计为单例模式,比如一个电脑有两台打印机,在输出的时候就要处理不能两台打印机打印同一个文件。
Singleton类:懒汉式,线程安全。
- 优点:第一次调用才初始化,避免内存浪费。
- 缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
Singleton类:饿汉式,线程安全
- 优点:没有加锁,执行效率会提高。
- 缺点:类加载时就初始化,浪费内存。
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
4、构建者模式
将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。例如:电脑是显示器、键盘、鼠标等等组成使用的,但客户只需购买就可以了,无需关注产品内部组成的细节。
- Product:最终要生成的对象,例如 Computer实例。
- Builder:构建者的抽象基类(有时会使用接口代替)。其定义了构建Product的抽象步骤,其实体类需要实现这些步骤。其会包含一个用来返回最终产品的方法Product getProduct()。
- ConcreteBuilder:Builder的实现类。
- Director:决定如何构建最终产品的算法. 其会包含一个负责组装的方法void Construct(Builder builder), 在这个方法中通过调用builder的方法,就可以设置builder,等设置完成后,就可以通过builder的 getProduct() 方法获得最终的产品。
PC类:电脑标准规范类
public class PC {
private String keyboard;//键盘
private String mouse;//鼠标
private String monitor;//显示器
private String master;//主机
public PC(String keyboard, String mouse) {
this.keyboard = mouse;
this.mouse = mouse;
}
public void setMonitor(String monitor) {
this.monitor = monitor;
}
public void setMaster(String master) {
this.master = master;
}
@Override
public String toString() {
return "PC{" +
"keyboard='" + keyboard + '\'' +
", mouse='" + mouse + '\'' +
", monitor='" + monitor + '\'' +
", master='" + master + '\'' +
'}';
}
}
PCBuilder类:抽象构建者类
public abstract class PCBuilder {
public abstract void setMonitor();
public abstract void setMaster();
public abstract PC getPC();
}
MacPCBuilder类:苹果电脑构建者类
public class MacPCBuilder extends PCBuilder {
private PC pc;
public MacPCBuilder(String keyboard, String mouse) {
pc = new PC(keyboard, mouse);
}
@Override
public void setMonitor() {
pc.setMonitor("苹果显示器");
}
@Override
public void setMaster() {
pc.setMaster("苹果主机");
}
@Override
public PC getPC() {
return pc;
}
}
MiPCBuilder类:小米电脑构建者类
public class MiPCBuilder extends PCBuilder {
private PC pc;
public MiPCBuilder(String keyboard, String mouse) {
pc = new PC(keyboard, mouse);
}
@Override
public void setMonitor() {
pc.setMonitor("小米显示器");
}
@Override
public void setMaster() {
pc.setMaster("小米主机");
}
@Override
public PC getPC() {
return pc;
}
}
PCDirector类:指导者类
public class PCDirector {
public void makePC(PCBuilder builder){
builder.setMonitor();
builder.setMaster();
}
}
演示:
public class Demo {
public static void main(String[] args) {
PCDirector director=new PCDirector();
PCBuilder builder=new MacPCBuilder("德国樱桃键盘","罗技鼠标");
director.makePC(builder);
PC macPC=builder.getPC();
System.out.println("mac pc:"+macPC.toString());
PCBuilder miPCBuilder=new MiPCBuilder("ikbc键盘","雷柏鼠标");
director.makePC(miPCBuilder);
PC miPC=miPCBuilder.getPC();
System.out.println("mi pc:"+miPC.toString());
}
}
控制台输出:
mac pc:PC{keyboard='罗技鼠标', mouse='罗技鼠标', monitor='苹果显示器', master='苹果主机'}
mi pc:PC{keyboard='雷柏鼠标', mouse='雷柏鼠标', monitor='小米显示器', master='小米主机'}
5、原型模式
将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例。
这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。
Shape类:实现了Cloneable接口的抽象类
public abstract class Shape implements Cloneable {
private String id;
protected String type;
abstract void buy();
public String getType(){
return type;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Object clone() {
Object clone = null;
try {
clone = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
Apple类:苹果类
public class Apple extends Shape {
public Apple(){
type = "Apple";
}
@Override
public void buy() {
System.out.println("buy() method: Apple");
}
}
Banana类:香蕉类
public class Banana extends Shape {
public Banana(){
type = "Banana";
}
@Override
public void buy() {
System.out.println("buy() method: Banana");
}
}
Orange类:橙子类
public class Orange extends Shape {
public Orange(){
type = "Orange";
}
@Override
public void buy() {
System.out.println("buy() method: Orange");
}
}
ShapeCache类:
public class ShapeCache {
private static HashMap<String, Shape> shapeMap = new HashMap<String, Shape>();
public static Shape getShape(String shapeId) {
Shape cachedShape = shapeMap.get(shapeId);
return (Shape) cachedShape.clone();
}
// 对每种水果都运行数据库查询,并创建该水果。shapeMap.put(shapeKey, shape);
public static void loadCache() {
Apple apple = new Apple();
apple.setId("1");
shapeMap.put(apple.getId(),apple);
Banana banana = new Banana();
banana.setId("2");
shapeMap.put(banana.getId(),banana);
Orange orange = new Orange();
orange.setId("3");
shapeMap.put(orange.getId(),orange);
}
}
演示:
public class Demo {
public static void main(String[] args) {
ShapeCache.loadCache();
Shape clonedShape = (Shape) ShapeCache.getShape("1");
System.out.println("Shape : " + clonedShape.getType());
Shape clonedShape2 = (Shape) ShapeCache.getShape("2");
System.out.println("Shape : " + clonedShape2.getType());
Shape clonedShape3 = (Shape) ShapeCache.getShape("3");
System.out.println("Shape : " + clonedShape3.getType());
}
}
控制台输出:
Shape : Apple
Shape : Banana
Shape : Orange