目录
设计模式的六大原则
- 开闭原则(Open Close Principle):开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。
- 里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对"开-闭"原则的补充。实现"开-闭"原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
- 依赖倒转原则(Dependence Inversion Principle:这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。
- 接口隔离原则(Interface Segregation Principle):这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。
- 迪米特法则(最少知道原则)(Demeter Principle):为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。
- 合成复用原则(Composite Reuse Principle):原则是尽量使用合成/聚合的方式,而不是使用继承
创建型模式
单例模式
单例模式主要是为了避免因为创建了多个实例造成资源的浪费,且多个实例由于多次调用容易导致结果出现错误,而使用单例模式能够保证整个应用中有且只有一个实例。
单例模式主要是为了避免因为创建了多个实例造成资源的浪费,且多个实例由于多次调用容易导致结果出现错误,而使用单例模式能够保证整个应用中有且只有一个实例。
-
定义:只需要三步就可以保证对象的唯一性
- (1) 不允许其他程序用new对象
- (2) 在该类中创建对象
- (3) 对外提供一个可以让其他程序获取该对象的方法
-
对比定义:
- (1) 私有化该类的构造函数
- (2) 通过new在本类中创建一个本类对象
- (3) 定义一个公有的方法,将在该类中所创建的对象返回
-
饿汉式[可用]:SingletonEHan.java
java
/**
* Created by jingbin on 2016/10/27.
* 1.单例模式的饿汉式[可用]
* (1)私有化该类的构造函数
* (2)通过new在本类中创建一个本类对象
* (3)定义一个公有的方法,将在该类中所创建的对象返回
* <p>
* 优点:从它的实现中我们可以看到,这种方式的实现比较简单,在类加载的时候就完成了实例化,避免了线程的同步问题。
* 缺点:由于在类加载的时候就实例化了,所以没有达到Lazy Loading(懒加载)的效果,也就是说可能我没有用到这个实例,但是它
* 也会加载,会造成内存的浪费(但是这个浪费可以忽略,所以这种方式也是推荐使用的)。
*/
public class SingletonEHan {
private SingletonEHan() {}
/**
* 1.单例模式的饿汉式[可用]
*/
private static SingletonEHan singletonEHan = new SingletonEHan();
public static SingletonEHan getInstance() {
return singletonEHan;
}
// SingletonEHan instance= SingletonEHan.getInstance();
/**
* 2. 单例模式的饿汉式变换写法[可用]
* 基本没区别
*/
private static SingletonEHan singletonEHanTwo = null;
static {
singletonEHanTwo = new SingletonEHan();
}
public static SingletonEHan getSingletonEHan() {
if (singletonEHanTwo == null) {
singletonEHanTwo = new SingletonEHan();
}
return singletonEHanTwo;
}
// SingletonEHan instance= SingletonEHan.getSingletonEHan();
}
- 含懒汉式[双重校验锁 推荐用]:SingletonLanHan.java
java
private SingletonLanHan() {}
private static SingletonLanHan singletonLanHanFour;
public static SingletonLanHan getSingletonLanHanFour() {
if (singletonLanHanFour == null) {
synchronized (SingletonLanHan.class) {
if (singletonLanHanFour == null) {
singletonLanHanFour = new SingletonLanHan();
}
}
}
return singletonLanHanFour;
}
- 内部类[推荐用]:SingletonIn.java
java
/**
* Created by jingbin on 2016/10/28.
* 7. 内部类[推荐用]
* <p>
* 这种方式跟饿汉式方式采用的机制类似,但又有不同。
* 两者都是采用了类装载的机制来保证初始化实例时只有一个线程。
* 不同的地方:
* 在饿汉式方式是只要Singleton类被装载就会实例化,
* 内部类是在需要实例化时,调用getInstance方法,才会装载SingletonHolder类
* <p>
* 优点:避免了线程不安全,延迟加载,效率高。
*/
public class SingletonIn {
private SingletonIn() {
}
private static class SingletonInHodler {
private static SingletonIn singletonIn = new SingletonIn();
}
public static SingletonIn getSingletonIn() {
return SingletonInHodler.singletonIn;
}
}
- 枚举[推荐用]:SingletonEnum.java
java
/**
* Created by jingbin on 2016/10/28.
* 8. 枚举[极推荐使用]
*
* 这里SingletonEnum.instance
* 这里的instance即为SingletonEnum类型的引用所以得到它就可以调用枚举中的方法了。
借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象
*/
public enum SingletonEnum {
instance;
private SingletonEnum() {
}
public void whateverMethod() {
}
// SingletonEnum.instance.method();
}
建造者模式
建造模式是对象的创建模式。建造模式可以将一个产品的内部表象(internal representation)与产品的生产过程分割开来,从而可以使一个建造过程生成具有不同的内部表象的产品对象。
-
需求:用户去汽车店购买汽车。
-
分析:汽车店根据每个用户的需求提取对应汽车
-
建造者超类:Builder
java
public abstract class Builder {
public abstract void setPart(String name, String type);
public abstract Product getProduct();
}
-
建造者对应实现类:ConcreteBuilder
java
public class ConcreteBuilder extends Builder {
private Product product = new Product();
@Override
public void setPart(String name, String type) {
product.setName(name);
product.setType(type);
}
@Override
public Product getProduct() {
return product;
}
}
- 店长Director取汽车:
java
// 店长
Director director = new Director();
// 得到宝马汽车,内部实现提取宝马汽车的详情操作
Product product = director.getBProduct();
// 展示汽车信息
product.showProduct();
工厂模式
简单列一下这个模式的家族:
-
1、静态工厂模式
- 这个最常见了,项目中的辅助类,TextUtil.isEmpty等,类+静态方法。
-
2、简单工厂模式(店里买肉夹馍)
- 定义:通过专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
- 根据类型直接创建肉夹馍:SimpleRoujiaMoFactory.java
java
/**
* Created by jingbin on 2016/10/23.
* 简单工厂模式
*/
public class SimpleRoujiaMoFactory {
public RoujiaMo creatRoujiaMo(String type) {
RoujiaMo roujiaMo = null;
switch (type) {
case "Suan":
roujiaMo = new ZSuanRoujiaMo();
break;
case "La":
roujiaMo = new ZLaRoujiaMo();
break;
case "Tian":
roujiaMo = new ZTianRoujiaMo();
break;
default:// 默认为酸肉夹馍
roujiaMo = new ZSuanRoujiaMo();
break;
}
return roujiaMo;
}
}
java
public RoujiaMo creatRoujiaMo(String type) {
RoujiaMo roujiaMo = null;
switch (type) {
case "Suan":
roujiaMo = new ZSuanRoujiaMo();
break;
case "La":
roujiaMo = new ZLaRoujiaMo();
break;
case "Tian":
roujiaMo = new ZTianRoujiaMo();
break;
default:// 默认为酸肉夹馍
roujiaMo = new ZSuanRoujiaMo();
break;
}
return roujiaMo;
}
-
3、工厂方法模式(开分店)
- 定义:定义一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法模式把类实例化的过程推迟到子类。
- 对比定义:
- 1、定义了创建对象的一个接口:public abstract RouJiaMo sellRoujiaMo(String type);
- 2、由子类决定实例化的类,可以看到我们的馍是子类生成的。
-
提供创建肉夹馍店抽象方法:RoujiaMoStore.java
java
/**
* Created by jingbin on 2016/10/24.
*
* 在北京和西安 开分店:
* 工厂方法模式:
* 定义:定义一个创建对象的接口,但由子类决定要实例化的类是哪一个。
* 工厂方法模式把类实例化的过程推迟到子类。
*
* 对照定义:
1、定义了创建对象的一个接口:public abstract RouJiaMo sellRoujiaMo(String type);
2、由子类决定实例化的类,可以看到我们的馍是子类生成的。
*/
public abstract class RoujiaMoStore {
public abstract RoujiaMo sellRoujiaMo(String type);
// public RoujiaMo sellRoujiaMo(String type) {
//
// RoujiaMo roujiaMo = creatRoujiaMo(type);
// roujiaMo.prepare();
// roujiaMo.fire();
// roujiaMo.pack();
// return roujiaMo;
//
// }
}
java
public abstract RoujiaMo sellRoujiaMo(String type);
- 具体实现抽象方法:XianRoujiaMoStore.java
java
/**
* Created by jingbin on 2016/10/24.
* 西安肉夹馍店 让分店自己去卖自己口味的肉夹馍
*/
public class XianRoujiaMoStore extends RoujiaMoStore {
private XianSimpleRoujiaMoFactory factory;
public XianRoujiaMoStore(XianSimpleRoujiaMoFactory factory) {
this.factory = factory;
}
public RoujiaMo sellRoujiaMo(String type) {
RoujiaMo roujiaMo = factory.creatRoujiaMo(type);
roujiaMo.prepare();
roujiaMo.fire();
roujiaMo.pack();
return roujiaMo;
}
// @Override
// public RoujiaMo creatRoujiaMo(String type) {
//
// RoujiaMo roujiaMo = null;
// switch (type) {
// case "suan":
// roujiaMo = new XianSuanRoujiMo();
// break;
// case "tian":
// roujiaMo = new XianKuRoujiMo();
// break;
// case "la":
// roujiaMo = new XianlaRoujiMo();
// break;
// default:// 默认为 西安 酸肉夹馍
// roujiaMo = new XianSuanRoujiMo();
// break;
// }
// return roujiaMo;
// }
}
- 分店依旧使用简单工厂模式:XianSimpleRoujiaMoFactory.java
java
/**
* Created by jingbin on 2016/10/23.
* 西安 简单工厂模式:
* 用来西安店生产自己店里的肉夹馍
*/
public class XianSimpleRoujiaMoFactory {
public RoujiaMo creatRoujiaMo(String type) {
RoujiaMo roujiaMo = null;
switch (type) {
case "Suan":
roujiaMo = new XianSuanRoujiMo();
break;
case "La":
roujiaMo = new XianKuRoujiMo();
break;
case "Tian":
roujiaMo = new XianlaRoujiMo();
break;
default:// 默认为酸肉夹馍
roujiaMo = new XianSuanRoujiMo();
break;
}
return roujiaMo;
}
}
抽象工厂模式
- 定义:提供一个接口,用于创建相关的或依赖对象的家族,而不需要明确指定具体类。
- 对比定义:
- 1、提供一个接口:public interface RouJiaMoYLFactroy
- 2、用于创建相关的或依赖对象的家族 public Meat createMeat();public YuanLiao createYuanliao();我们接口用于创建一系列的原材料。
- 创建用于提供原料的接口工厂:RoujiaMoYLFactory.java
java
/**
* Created by jingbin on 2016/10/26.
* 4、抽象工厂模式:
* 定义:提供一个接口,用于创建相关的或依赖对象的家族,而不需要明确指定具体类。
* 这定义有点绕口,算了,还是拿例子来说。
* 继续卖肉夹馍,咱们生意这么好,难免有些分店开始动歪脑子,开始使用劣质肉等,砸我们的品牌。
* 所以我们要拿钱在每个城市建立自己的原料场,保证高质量原料的供应。
*/
public interface RoujiaMoYLFactory {
/**
* 生产肉
*/
public Meet creatMeet();
/**
* 生产一些原料
*/
public YuanLiao creatYuanLiao();
}
- 各自分店实现接口,完成原料提供:XianRoujiaMoYLFoctory.java
java
/**
* Created by jingbin on 2016/10/26.
* 西安的肉夹馍原料工厂,是西安的特色原料,还有其他分店的特色原料
*/
public class XianRoujiaMoYLFoctory implements RoujiaMoYLFactory {
@Override
public Meet creatMeet() {
return new XianFreshMeet();
}
@Override
public YuanLiao creatYuanLiao() {
return new XianTeSeYuanLiao();
}
}
- 准备时,使用官方的原料:RoujiaMo.java
java
import android.util.Log;
/**
* Created by jingbin on 2016/10/22.
*/
public abstract class RoujiaMo {
protected String name;
/**
* 准备工作
*/
public void prepare(RoujiaMoYLFactory roujiaMoYLFactory) {
Meet meet = roujiaMoYLFactory.creatMeet();
YuanLiao yuanLiao = roujiaMoYLFactory.creatYuanLiao();
Log.e("---RoujiaMo:", "使用官方的原料 ---" + name + ": 揉面-剁肉-完成准备工作 yuanLiao:"+meet+"yuanLiao:"+yuanLiao);
}
/**
* 秘制设备--烘烤2分钟
*/
public void fire() {
Log.e("---RoujiaMo:", name + ": 肉夹馍-专用设备-烘烤");
}
/**
* 使用你们的专用袋-包装
*/
public void pack() {
Log.e("---RoujiaMo:", name + ": 肉夹馍-专用袋-包装---end");
}
}
java
/**
* 准备工作
*/
public void prepare(RoujiaMoYLFactory roujiaMoYLFactory) {
Meet meet = roujiaMoYLFactory.creatMeet();
YuanLiao yuanLiao = roujiaMoYLFactory.creatYuanLiao();
Log.e("---RoujiaMo:", "使用官方的原料 ---" + name + ": 揉面-剁肉-完成准备工作 yuanLiao:"+meet+"yuanLiao:"+yuanLiao);
}
原型模式
原型模式是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。
以获取多种形状为例,共分四步:
- 1、创建一个实现了 Cloneable 接口的抽象类。Shape(implements Cloneable)
java
public abstract class Shape implements Cloneable {
private String id;
protected String type;
public abstract void draw();
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public Object clone() {
Object object = null;
try {
object = super.clone();
} catch (CloneNotSupportedException e) {
Log.e("--", e.getMessage());
}
return object;
}
}
2、创建扩展了上面抽象类的实体类。Circle、Rectangle、Square
java
public class Circle extends Shape {
public Circle() {
type = "Circle";
}
@Override
public void draw() {
Log.e("---", "Inside Circle::draw() method.");
}
}
java
/**
* Created by jingbin on 2020-01-31.
* 2. 创建扩展了上面抽象类的实体类。Rectangle 矩形
*/
public class Rectangle extends Shape {
public Rectangle() {
type = "Rectangle";
}
@Override
public void draw() {
Log.e("---", "Inside Rectangle::draw() method.");
}
}
java
/**
* Created by jingbin on 2020-01-31.
* 2. 创建扩展了上面抽象类的实体类。Square 正方形
*/
public class Square extends Shape {
public Square() {
type = "Square";
}
@Override
public void draw() {
Log.e("---", "Inside Square::draw() method.");
}
}
3、创建一个类,从数据库获取实体类,并把它们存储在一个 Hashtable 中。ShapeCache
java
public class ShapeCache {
private static Hashtable<String, Shape> shapeMap = new Hashtable<String, Shape>();
public static Shape getShape(String shapeId) {
Shape shapeCache = shapeMap.get(shapeId);
return (Shape) shapeCache.clone();
}
// 对每种形状都运行数据库查询,并创建该形状
// shapeMap.put(shapeKey, shape);
// 例如,我们要添加三种形状
public static void loadCache() {
Circle circle = new Circle();
circle.setId("1");
shapeMap.put(circle.getId(), circle);
Rectangle rectangle = new Rectangle();
rectangle.setId("2");
shapeMap.put(rectangle.getId(), rectangle);
Square square = new Square();
square.setId("3");
shapeMap.put(square.getId(), square);
}
}
4、使用 ShapeCache 类来获取存储在 Hashtable 中的形状的克隆。
java
// 使用 ShapeCache 类来获取存储在 Hashtable 中的形状的克隆。
ShapeCache.loadCache();
Shape shapeCache1 = ShapeCache.getShape("1");
Shape shapeCache2 = ShapeCache.getShape("2");
Shape shapeCache3 = ShapeCache.getShape("3");
参考材料
相关代码github地址:GitHub - youlookwhat/DesignPattern: 📚 Java 23种设计模式全归纳