设计模式分类:
- 创建形模式:单例模式、抽象工厂模式、建造者模式、工厂模式、原型模式
- 结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式
- 行为型模式:模板方法模式、命令模式、迭代器模式、观察者模式、终结者模式、备忘录模式、解释器模式、状态模式、策略模式
单例模式(Singleton Pattern):
该模式是Java中最简单的设计模式之一,属于创建形模型,提供了一种创建对象的最佳方式。这种模型涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
需要注意:
- 单例类只能有一个实例
- 单例类必须自己创建自己的唯一实例
- 单例类必须给所有其他对象提供这一实例
优点:
- 在内存里只有一个实例,减少内存的开销,尤其是频繁的创建和销毁实例
- 避免对资源的多重占用(比如写文件操作)
缺点:
- 没有接口
- 不能继承
- 一个类应该只关心内部逻辑,而不关心外面怎么实例化,与单一职责原则冲突
具体一个实现方式:
创建一个SingleObject
- 在该类中实例化一个该类的对象instance
- 添加一个私有化的构造方法,使得无法在该类外new一个对象
- 添加一个静态方法返回该类的实例对象instance。(仅通过类名调用该方法,获得instance这个对象,每次获取的对象都是同一个对象)
public class SingleObject { private static SingleObject instance = new SingleObject(); //私有化的构造函数,这样该类就不会被实例化(无法new一个对象) private SingleObject() { } //获取唯一可用的对象 public static SingleObject getInstance() { return instance; } //单例模式中的方法 public void doit() { System.out.println("单例模式:成员方法"); } }
几种实现方式:懒汉式(在静态方法中初始化实例,以时间换空间);饿汉式(在声明对象的时就初始化,以空间换时间)
1、懒汉式:不支持多线程。
public class SingleObjectLazy { private static SingleObjectLazy instance; private SingleObjectLazy() { } public static SingleObjectLazy getInstance() { if(instance == null) { instance = new SingleObjectLazy(); } return instance; } }
2、懒汉式:支持线程安全,第一次调用时初始化,避免内存浪费,通过synchronized修饰getInstance方法保证线程安全。
public class SingleObjectLazy { private static SingleObjectLazy instance; private SingleObjectLazy() { } public static synchronized SingleObjectLazy getInstance() { if(instance == null) { instance = new SingleObjectLazy(); } return instance; } }
3、饿汉式:支持线程安全,不需要加锁,执行效率高,但是在类加载时就初始化,浪费内存。(比较常用的一种)
public class Singleton { private static Singleton instance = new Singleton(); private Singleton (){} public static Singleton getInstance() { return instance; } }
4、
5、
工厂模式(Factory Method):
该模式属于创建形模式,提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且通过使用一个共同的接口来指向新创建的对象。简言之:工厂就是负责生产产品的,在设计模式中,可以理解为负责生产对象的一个类,就是工厂类。
模式组成:
- 抽象产品:具体产品的父类,所有的具体产品都要实现该接口或者继承该类
- 具体产品:抽象产品的子类,就是工厂类要创建的目标类
- 工厂:被外界调用,根据调用不同的方法产生不同的产品类的实例
缺点:
- 每增加一个产品,都需要增加一个具体来和对象实现工厂,使得系统中类的个数成倍增加,在一定成都上增加了系统的复杂度,同时也增加了系统具体类的依赖。
注意事项:作为一种创建模式,在壬戌需要生成复杂对象的地方都可以使用工厂模式。复杂对象适合使用工厂模式,而简单对象(只需要通过new实例化的)无需使用工厂模式。如果使用工厂模式就需要引入一个工厂类,会增加系统的复杂度。
for example:一个工厂生产A、B、C、D类的商品。以工厂模式实现A、B、C、D这四款商品
步骤1:创建一个生产商品的抽象类,也可以是接口,并定义生产商品的抽象方法
public abstract class Product { public abstract void product(); }
步骤2:商品A、B、C分别继承Product类,并实现抽象方法
class ProductA extends Product{ @Override public void product() { // TODO 自动生成的方法存根 System.out.println("生产一个产品A"); } } class ProductB extends Product{ @Override public void product() { // TODO 自动生成的方法存根 System.out.println("生产一个产品B"); } } class ProductC extends Product{ @Override public void product() { // TODO 自动生成的方法存根 System.out.println("生产一个产品C"); } }
步骤3:创建一个工厂,通过创建静态方法从而根据传入不同参数创建不同具体产品的实例
public class Factory { public static Product Manufacture(String ProductName) { switch(ProductName) { case "A": return new ProductA(); case "B": return new ProductB(); case "C": return new ProductC(); default: return null; } } }
优点:
- 将创造实例的工作与使用实例的工作分开,使用者不必关心类对象如何创建
- 把初始化实例时的工作放到工厂里面,使得代码更容易维护。更符合面向对象的原则以及面向接口编程
缺点:
- 工厂类集中了所有实例(产品)的创建逻辑,一旦这个工厂不能正常工作,整个系统都会到影响
- 违背了“开放-关闭原则”,一旦添加新产品就不得不修改工厂类的逻辑,这样就会造成工厂逻辑过于复杂
- 简单工厂模式由于使用了静态工厂方法,静态方法不能被继承和重写,会造成工厂角色无法形成基于继承的等级结构。
工厂方法模式:
与简单工厂模式的不同在于:将类的实例化(具体产品的创建)延迟到工厂类的子类(具体工厂)中完成,即由子类来决定该实例化哪一个类。实际上就是每一个都有其自己的子工厂,该子工厂继承工厂父类。
模式组成:
- 抽象产品(Product):具体产品的父类,每个产品都继承这个类,并实现其中的方法
- 具体产品:抽象产品的子类,描述生产的具体产品
- 抽象工厂(Factory):具体工厂的父类,每个子工厂都继承该类,并添加其中的方法
- 具体工厂:抽象工厂的子类,描述具体工厂的功能
例如:一个工厂只生产A产品,此时同时想生产B产品,要改变原工厂的配置比较困难,且要生产更多的产品就要一次一次的修改,因此通过办理分厂的方式来生产B产品
步骤1:工厂父类
public abstract class Factory { public abstract void factory(); }
步骤2:产品父类
public abstract class Product { public abstract void product(); }
步骤3:具体产品类,继承产品类,并实现其中的方法
class ProductA extends Product{ @Override public void product() { System.out.println("生产一个A产品"); } } class ProductB extends Product{ @Override public void product() { System.out.println("生产一个B产品"); } }
步骤4:具体产品的子工厂继承工厂父类,并实现其中方法
class FactoryA extends Factory { @Override public Product factory() { return new ProductA(); } } class FactoryB extends Factory { @Override public Product factory() { return new ProductB(); } }
步骤5:外界通过调用具体工厂类的方法,从而创建不同具体产品类的实例
public class Main { public static void main(String[] args) { //想要A产品 Factory Factory = new FactoryA(); Factory.factory().product(); //又想要B产品 Factory = new FactoryB(); Factory.factory().product(); } } //输出 生产一个A产品 生产一个B产品
优点:
- 更符合开闭原则:新增一种产品时,只需要增加相应的具体产品类和相应的工厂子类即可(简单工厂模式需要修改工厂类的判断逻辑)
- 符合单一职责原则:每个具体子工厂类只负责创建对应的产品(简单工厂模式中的工厂类存在复杂的switch逻辑判读)
- 没有使用静态工厂方法,可以形成基于继承的等级结构(简单工厂中的工厂类使用静态工厂方法,不能形成基于继承的等级结构)
缺点:
- 在增加一个新的产品时候,除了要增加新产品类外,还要提供与之对应的具体工厂类,系统的类个数将会成对的增加,在一定程度上增加了系统的复杂度。同时,有更多类需要编译和运行,会给系统带来额外的开销
- 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行等译,增加了系统的抽象性和理解难度,且在实现时可能会需要用到DOM、反射等技术,增加了系统的实现难度。
- 虽然保证了工厂方法内的对修改关闭,但是对于使用工厂方法的类,如果要更换另外一种产品,任然需要修改实例化的具体工厂
- 一个具体工厂只能创建一种具体产品
建造者模式:
建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象,这种类型的设计模式数据创建型模式,提供了一种创建对象的最佳方式。一个Builder类会一步一步构造最终的对象,该Builder类是独立于其他对象的
一个实例帮助理解:一个套餐(汉堡,薯条,可乐)
主要作用:
- 方便用户创建复杂的对象(不需要知道实现的过程)
- 代码复用性&封装性(将对象构建过程的细节进行封装和复用)
模式:
- 指挥者(Director)直接和客户(Client)进行需求沟通
- 沟通后指挥者将客户创建产品的需要划分为各个部件的建造请求(Builder)
- 将各个部件的建造者请求委派到具体的建造者(ConcreteBuilder)
- 各个具体建造者负责进行产品部件的构建
- 最终构建成具体的产品
搞个实例好理解:首先是人类,人是由头、手、脚、身体构成(假设就是这样)。因此先准备这些类:人,头,手,脚,身体,因为不同的人有不同的构造,因此零部件为抽象类
//人 public class People { private Head head; private Hand hand; private Foot foot; private Body body; public Head getHead() { return head; } public void setHead(Head head) { this.head = head; } public Hand getHand() { return hand; } public void setHand(Hand hand) { this.hand = hand; } public Foot getFoot() { return foot; } public void setFoot(Foot foot) { this.foot = foot; } public Body getBody() { return body; } public void setBody(Body body) { this.body = body; } } //头 public abstract class Head { public abstract void head(); } //手 public abstract class Hand { public abstract void hand(); } //脚 public abstract class Foot { public abstract void foot(); } //身体 public abstract class Body { public abstract void body(); }
Builder类:创造不同类型的人的父类,抽象类
public abstract class Builder { public abstract void buildHead(); public abstract void buildHand(); public abstract void buildFoot(); public abstract void buildBody(); public abstract People buildPeople(); }
具体的Builder类:(假设是要创造一个巨人类)
public class HugeManBuilder extends Builder{ private People people = new People(); @Override public void buildHead() { // TODO 自动生成的方法存根 people.setHead(new HugeHead()); } @Override public void buildHand() { people.setHand(new HugeHand()); } @Override public void buildFoot() { people.setFoot(new HugeFoot()); } @Override public void buildBody() { people.setBody(new HugeBody()); } @Override public People buildPeople() { return people; } } 零部件的具体实现类: public class HugeHead extends Head{ @Override public void head() { System.out.println("生成一个巨大的头"); } } public class HugeHand extends Hand{ @Override public void hand() { System.out.println("生成一个巨大的手"); } } public class HugeFoot extends Foot { @Override public void foot() { System.out.println("生成一个巨大的脚"); } } public class HugeBody extends Body { @Override public void body() { System.out.println("生成一个巨大的身体"); } }
指挥者类:Director类,可以指定建造的顺序
public class Director { private Builder builder = null; public Director(Builder builder) { this.builder = builder; } public People construct() { builder.buildHead(); builder.buildHand(); builder.buildFoot(); builder.buildBody(); return builder.buildPeople(); } }
在外部,我想创建一个实例化一个巨人:
public class Main { public static void main(String[] args) { // TODO 自动生成的方法存根 Builder builder = new HugeManBuilder(); Director director = new Director(builder); People hugeMan = director.construct();//调用construct方法,创建一个巨人 show(hugeMan); } //显示巨人的信息 public static void show(People people) { people.getHead().head(); people.getHand().hand(); people.getFoot().foot(); people.getBody().body(); } } //输出 生成一个巨大的头 生成一个巨大的手 生成一个巨大的脚 生成一个巨大的身体
上述例子是建造者模式的常规用法,Director类主要用于指导具体构建者如何构建产品,可以控制调用的先后次序,并向调用者返回完整的产品类。如果我还想创造小矮类,创建一个SmallManBuilder extends Builder,并实现其中的方法,同时要分别添加SmallHead extends Head、SmallHand extends Hand、SmallFoot extends Foot、SmallBody extends Body,并分别实现其中的抽象方法。在外部,将SmallManBuilder作为Director构造方法的传入参数类型。
优点:
- 使用建造者模式可以使客户端不必知道产品内部组成的细节
- 具体的建造者类之间是相互独立的,这有利于系统的扩展
- 具体的建造者相互独立,因此可以对建造的过程逐步细化,而不会对其他模块产生任何影响
缺点:
- 建造者模式所创建的产品一般具有较多的共同点,其余组成部分相似;如果产品之间的差异性很大,就不适合使用建造者模式,因此该模式的使用范围有一定的限制
- 如果产品的内部变化复杂,可能会导致需要定义很多具体的建造者类来实现这种变化,导致系统变得很庞大。
再总结一下:建造者模式,是将一个复杂的对象的构建与它的表示分离。先抽取一类事物的共性,例如人,都有胳膊有腿,那么这就是人的共性,而不同的人的胳膊腿有不同的形态,就需要分别实现。Builder类就是一个抽象的建造者,为一个人建造胳膊腿等。而不同的人就有不同的建造方式,那么就要继承Builder实现自己的建造方式。指挥者(Director)就将客户的需求划分为各个部件的建造请求,将各个部件的建造请求委派到具体的建造者,建造胳膊腿等,通过传入的具体建造者来确定建造什么样的。大概就是这个过程。
适配器模式:
代理模式:
模板方法模式:
策略模式: