架构师之面向对象的设计模式

发布于: 2020 年 06 月 24 日

设计模式的作用

设计模式的定义

什么是设计模式
  • 每一种模式都描述了一种问题的通用解决方案。这种问题在我们的环境中,不停的出现。

  • 设计模式是一种可重复使用的解决方案。

一个设计模式的四个部分
  • 模式的名称-由少量的字组成的名称,有助于我们表达我们的设计。

  • 待解问题-描述了何时需要运用这种模式,以及运用模式的环境(上下文)。

  • 解决方案-描述了组成设计的元素(类和对象)、他们的关系、职责以及合作。但这种解决方案是抽象的,他不代表具体的实现。

  • 结论-运用这种方案所带来的利和弊。主要是指它对系统的弹性、扩展性、和可移植性的影响。

设计模式的分类

从功能分
  • 创建模式(Creational Patterns)

对类的实例化过程的抽象。

  • 结构模式(Structural Patters)

将类或者对象结合在一起形成更大的结构。

  • 行为模式(Behavioral Patters)

对在不同的对象之间划分责任和算法的抽象化。

从方式分
  • 类模式

以继承的方式实现模式,静态的。

  • 对象模式

以组合的方式实现模式,动态的。

Singleton 单例模式

Singleton模式保证产生单一实例,就是说一个类只产生一个实例。使用singleton有两个原因:

  • 是因为只有一个实例,可以减少实例频繁创建和销毁带来的资源消耗;

  • 是当多个用户使用这个实例的时候,便于进行统一控制(比如打印机对象)

前者是性能需求,后者是功能需求。

饿汉模式:

//饿汉模式:线程安全,调用效率高。但是不能延时加载
//由于该模式在加载类的时候对象就已经创建了,所以加载类的速度比较慢,但是获取对象的速度比较快,且是线程安全的。
public class Singleton {
private Singleton() {
}
private static Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
}

懒汉模式:

//由于该模式是在运行时加载对象的,所以加载类比较快,但是对象的获取速度相对较慢,且线程不安全。如果想要线程安全的话可以加上synchronized关键字,但是这样会付出惨重的效率代价。
public class Singleton {
//线程不安全的
private static Singleton instance = null;
private Singleton() {
}
//运行时加载对象
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
//进阶版
// 如果不加synchronized 很可能会产生多重实例 也就是线程不安全的原因
public class Singleton {
//线程不安全的
private static Singleton instance = null;
private Singleton() {
}
//运行时加载对象
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}

说明:

  1. 一定要有私有的构造函数,保证类实例只能通过getInstance()方法获得。

  2. 懒汉模式getInstance对的修饰符一定要加上synchronized,否则可能会产生多重实例。

  3. 尽量使用饿汉模式构建单例。

  4. 单例中的成员变量是多线程重用的,可能会产生意想不到的结果,因此尽量将单例设计为无状态对象(只提供服务,不保存状态)。

适配器模式(Adapter)

将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。在适配器模式中,我们通过增加一个新的适配器类来解决接口不兼容的问题,使得原本没有任何关系的类可以协同工作。

根据适配器类与适配者类的关系不同,适配器模式可分为对象适配器和类适配器两种,在对象适配器模式中,适配器与适配者之间是关联关系;在类适配器模式中,适配器与适配者之间是继承(或实现)关系。

角色

Target(目标抽象类):目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。

Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系。

Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。

类适配器

//适配者类
public class Adaptee {
public void adapteeRequest() {
System.out.println("被适配者的方法");
}
}
//定义一个目标接口
public interface Target {
void request();
}
public class Adapter extends Adaptee implements Target{
@Override
public void request() {
//...一些操作...
super.adapteeRequest();
//...一些操作...
}
}
public class Test {
public static void main(String[] args) {
Target adapterTarget = new Adapter();
adapterTarget.request();
}
}
//被适配者的方法

对象适配器

public class Adapter implements Target{
// 适配者是对象适配器的一个属性
private Adaptee adaptee = new Adaptee();
@Override
public void request() {
//...
adaptee.adapteeRequest();
//...
}
}

适配器模式总结

主要优点:

将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无须修改原有结构。

增加了类的透明性和复用性,将具体的业务实现过程封装在适配者类中,对于客户端类而言是透明的,而且提高了适配者的复用性,同一个适配者类可以在多个不同的系统中复用。

灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”。

策略模式(Strategy)

  1. 策略与if-else语句

if-else是一个平等的功能结构,要么if要么else,if-else从运行地位上来讲是平等的。而策略模式就是把各个平等的具体实现封装到单独的策略实现类类,然后通过上下文来与具体的策略类进行交互。

  1. 算法的平等性

策略模式一个很大的特点就是各个策略算法的平等性。对于一系列具体的策略算法,大家的地位是完全一样的,正式因为这个平等性,才能实现算法之间的可以相互替换。

报价管理,如果针对不同的客户,打折情况不一样的情况下通常我们会通过if-else来实现。

代码如下:

/**
* @Classname Price
* @Description 根据不同的客户有不同的价格
* @Date 2020/6/28 3:20 PM
* @Author by lixin
*/
public class Price {
//根据客户类型打折
public Double quote(Double goodPrice, String customerType) {
if ("普通客户".equals(customerType)){
return goodPrice;
}else if ("会员".equals(customerType)){
return goodPrice*0.88;
}else if ("企业客户".equals(customerType)){
return goodPrice*0.8;
}
return null;
}
}

大量的if-else可以实现我们的业务逻辑,但是扩展性不好,维护起来也不是很方便,代码越来越的时候,开发者自己都很难梳理清楚代码。牵一发动全身,而且需求增加等问题的出现时,都需要去改变原来的类,违反了开闭原则

策略模式代码如下:

public class Price {
private Strategy strategy = null;
/**
* @Description 构造方法通过传入不同的策略对象实现不同的策略算法
* @param strategy
* @return
* @date 2020/6/29 2:45 PM
* @auther lixin
*/
public Price(Strategy strategy) {
this.strategy = strategy;
}
public Double quote(Double goodPrice) {
return this.strategy.calcPrice(goodPrice);
}
}

各种不同的客户的具体算法实现代码如下:

/**
* @Classname BigCustomStrategy
* @Description TODO
* @Date 2020/6/29 2:40 PM
* @Author by lixin
*/
public class BigCustomStrategy implements Strategy{
public double calcPrice(double goodPrice) {
System.out.println("我是企业大客户,我能打8折");
return goodPrice*0.8;
}
}
/**
* @Classname NewCustomStrategy
* @Description TODO
* @Date 2020/6/29 2:34 PM
* @Author by lixin
*/
public class NewCustomStrategy implements Strategy {
/**
* @Description
* @param goodPrice
* @return double
* @date 2020/6/29 2:34 PM
* @auther lixin
*/
public double calcPrice(double goodPrice) {
System.out.println("我是新客户,不打折");
return goodPrice;
}
}
/**
* @Classname VipCustomStrategy
* @Description TODO
* @Date 2020/6/29 2:38 PM
* @Author by lixin
*/
public class VipCustomStrategy implements Strategy {
public double calcPrice(double goodPrice) {
System.out.println("我是VIP中P,我打了8.8折");
return goodPrice*0.88;
}
}

客户端调用代码如下:

/**
* @Classname Test
* @Description TODO
* @Date 2020/6/29 2:46 PM
* @Author by lixin
*/
public class Test {
public static void main(String[] args) {
//创建具体的策略对象
Strategy strategy = new BigCustomStrategy();
//创建上下文
Price price = new Price(strategy);
//计算报价
Double quote = price.quote(1000.0);
//向客户报价
System.out.println("向客户报价:"+quote);
}
}
// 打印结果:
//我是企业大客户,我能打8折
//向客户报价:800.0

总结:通过策略模式根据不同的策略对象实现不同的算法,进行计算,这样不仅代码维护性好,可扩展性好,遵循了开闭原则和接口单一原则。

用户头像

彭阿三

关注

java工程师 2019.06.28 加入

一个慵懒的程序员。 关注公众号:程序那点事

评论

发布
暂无评论
架构师之面向对象的设计模式