设计模式

设计模式

设计模式的六大原则

1、开闭原则(Open Close Principle)

开闭原则的意思是:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。

2、里氏代换原则(Liskov Substitution Principle)

里氏代换原则是面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

3、依赖倒转原则(Dependence Inversion Principle)

这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。

4、接口隔离原则(Interface Segregation Principle)

这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。

5、迪米特法则,又称最少知道原则(Demeter Principle)

最少知道原则是指:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。

6、合成复用原则(Composite Reuse Principle)

合成复用原则是指:尽量使用合成/聚合的方式,而不是使用继承。

设计模式的类型

  • 创建型模式(这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活)
    • 工厂模式(Factory Pattern)
    • 抽象工厂模式(Abstract Factory Pattern)
    • 单例模式(Singleton Pattern)
    • 建造者模式(Builder Pattern)
    • 原型模式(Prototype Pattern)
  • 结构型模式(这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式)
    • 适配器模式(Adapter Pattern)
    • 桥接模式(Bridge Pattern)
    • 过滤器模式(Filter、Criteria Pattern)
    • 组合模式(Composite Pattern)
    • 装饰器模式(Decorator Pattern)
    • 外观模式(Facade Pattern)
    • 享元模式(Flyweight Pattern)
    • 代理模式(Proxy Pattern)
  • 行为型模式(这些设计模式特别关注对象之间的通信)
    • 责任链模式(Chain of Responsibility Pattern)
    • 命令模式(Command Pattern)
    • 解释器模式(Interpreter Pattern)
    • 迭代器模式(Iterator Pattern)
    • 中介者模式(Mediator Pattern)
    • 备忘录模式(Memento Pattern)
    • 观察者模式(Observer Pattern)
    • 状态模式(State Pattern)
    • 空对象模式(Null Object Pattern)
    • 策略模式(Strategy Pattern)
    • 模板模式(Template Pattern)
    • 访问者模式(Visitor Pattern)
  • J2EE 模式(这些设计模式特别关注表示层。这些模式是由 Sun Java Center 鉴定的)
    • MVC 模式(MVC Pattern)
    • 业务代表模式(Business Delegate Pattern)
    • 组合实体模式(Composite Entity Pattern)
    • 数据访问对象模式(Data Access Object Pattern)
    • 前端控制器模式(Front Controller Pattern)
    • 拦截过滤器模式(Intercepting Filter Pattern)
    • 服务定位器模式(Service Locator Pattern)
    • 传输对象模式(Transfer Object Pattern)

常用模式

1. 责任链模式

就是按照链的顺序执⾏⼀个个处理⽅法,链上的每⼀个任务都持有它后⾯那个任务的对象引⽤,以⽅便⾃⼰这段执⾏完成之后,调⽤其后⾯的处理逻辑。(例如Springboot的拦截器、过滤器链实现)

实现1:

定义任务接口

1
2
3
public interface Task {
public void run();
}

任务1实现接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Task1 implements Task{
private Task task;
public Task1() {}
public Task1(Task task){
this.task = task;
}
@Override
public void run() {
System.out.println("task1 is run");
if(task != null){
task.run();
}
}
}

任务2实现接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Task2 implements Task{
private Task task;
public Task2() {}
public Task2(Task task){
this.task = task;
}
@Override
public void run() {
System.out.println("task2 is run");
if(task != null){
task.run();
}
}
}

任务3实现接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Task3 implements Task{
private Task task;
public Task3() {}
public Task3(Task task){
this.task = task;
}
@Override
public void run() {
System.out.println("task3 is run");
if(task != null){
task.run();
}
}
}

taskChain,将要执行的任务通过指定的顺序串联起来

1
2
3
4
5
6
7
8
public class TaskChain{
public void runChain(){
Task task3 = new Task1();
Task task2 = new Task2(task3);
Task task1 = new Task3(task2);
task1.run();
}
}

主程序测试

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args)  {
TaskChain chain = new TaskChain();
chain.runChain();
/**打印:
task1 is run
task2 is run
task3 is run
**/
}

实现2(常用):

定义任务接口

1
2
3
public interface Task {
public void run();
}

任务1实现接口,并用@Order(1)指定注入顺序为1

1
2
3
4
5
6
7
@Order(1)
@Component
public class Task1 implements Task {
public void run() {
System.out.println("task1 is run");
}
}

任务2实现接口,并用@Order(2)指定注入顺序为2

1
2
3
4
5
6
7
@Order(2)
@Component
public class Task2 implements Task {
public void run() {
System.out.println("task3 is run");
}
}

任务3实现接口,并用@Order(3)指定注入顺序为3

1
2
3
4
5
6
7
@Order(2)
@Component
public class Task3 implements Task {
public void run() {
System.out.println("task3 is run");
}
}

taskService类,Spring的ioc容器支持通过Map或者List来直接注入对象,省去自己排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Component
public class TaskChainService {

@Autowired
private List<Task> taskList;

/**
* 执行处理
* @param context
* @return
*/
public void execute(){
for (Task task : taskList) {
task.run();
}
}
}

主程序测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@RunWith(SpringRunner.class)
@SpringBootTest
public class TaskServiceTest {

@Autowired
private TaskChainService taskChainService;

@Test
public void test(){
taskChainService.execute();
/**打印:
task1 is run
task2 is run
task3 is run
**/
}

}

2. 单例模式

保证某个类在程序中有且只有一个对象并且提供一个全局访问点,分为两种:饿汉式和懒汉式,饿汉式(例如:spring 中的 bean 默认是单例的而且,默认是饿汉式,也就是说在IOC容器启动时,bean 就被实例化了)

实现1(懒汉式):

懒汉式模式是线程不安全的,不支持多线程

1
2
3
4
5
6
7
public class Singleton {  
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}

实现2(懒汉式+synchronized )

懒汉式必须加synchronized才能才能保证单例,但加锁会影响效率。getInstance() 的性能对应用程序不是很关键(该方法使用不太频繁)

1
2
3
4
5
6
7
8
9
10
public class Singleton {  
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}

实现3(饿汉式):

饿汉式可以保证线程安全,但是在类加载时就初始化,浪费内存,特殊情况下依然还是会有线程安全的隐患

1
2
3
4
5
6
7
public class Singleton {  
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}

实现4(饿汉式+双重检测锁):

双检锁可以很好的解决线程问题,而且还能保持高的性能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Singleton {  
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}

实现5(饿汉式+双重检测锁+防止反射):

双重检测锁可以很好的保证了多线程的安全,但是依然可以通过反射来修改构造器privite的类型,所以这里依然可以通过私有构造器内部捕获反射的方式来有效防止

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Singleton {  
private volatile static Singleton singleton=null;
private Singleton (){
if(singleton!=null){
throw new RuntimeException("禁止反射创建")
}
}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}

实现6(枚举):

通过枚举不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化,而且还能防止反射攻击,就是比较少用

1
2
3
4
5
public enum Singleton {  
INSTANCE;
public void whateverMethod() {
}
}

3. 代理模式

为其他对象提供一种代理以控制对这个对象的访问(例如:Spring AOP)

正常流程:

  • 我:我-》准备结婚工作(包含彩排、礼物购买、人员分工)-》结婚-》结婚完毕,收拾现场

静态代理流程:

  • 我:我-》结婚
  • 代理人(婚庆公司):准备结婚-》结婚完毕,收拾现场

我只需要专注做结婚的事情其余的又我的代理人来执行即婚庆公司

实现1(静态代理):

定义代理接口,即要做的事

1
2
3
public interface  ProxyInterface  {
void marry();
}

婚庆公司需要做的事

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class WeddingCompany implements ProxyInterface {
private ProxyInterface proxyInterface;
public WeddingCompany (ProxyInterface proxyInterface) {
this.proxyInterface = proxyInterface;
}
@Override
public void marry() {
System.out.println("我们是婚庆公司的");
System.out.println("我们在做结婚前的准备⼯作");
System.out.println("节⽬彩排...");
System.out.println("礼物购买...");
System.out.println("⼯作⼈员分⼯...");
System.out.println("可以开始结婚了");
proxyInterface.marry();
System.out.println("结婚完毕,我们需要做后续处理,你们可以回家了,其余的事情我们公司来做");
}
}

我只专注做结婚的事情

1
2
3
4
5
6
public class My implements ProxyInterface {
@Override
public void marry() {
System.out.println("我结婚啦〜");
}
}

主程序运行

1
2
3
4
public static void main (String[] args) {
ProxyInterface proxyInterface = new WeddingCompany(new My());
proxyInterface.marry();
}

实现2(JDK动态代理):

定义代理接口,即要做的事

1
2
3
public interface ProxyInterface  {
void marry();
}

婚庆公司需要做的事

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class WeddingCompany implements InvocationHandler{
//被代理的接口
private ProxyInterface proxyInterface;
public WeddingCompany (ProxyInterface proxyInterface) {
this.proxyInterface = proxyInterface;
}

// 生成得到代理类
public Object getProxy() {
return Proxy.newProxyInstance(
this.getClass().getClassLoader(), proxyInterface.getClass().getInterfaces(), this
);
}
// 处理代理实例, 并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 添加方法
System.out.println("我们是婚庆公司的");
System.out.println("我们在做结婚前的准备⼯作");
System.out.println("节⽬彩排...");
System.out.println("礼物购买...");
System.out.println("⼯作⼈员分⼯...");
System.out.println("可以开始结婚了");
Object result = method.invoke(proxyInterface, args);
System.out.println("结婚完毕,我们需要做后续处理,你们可以回家了,其余的事情我们公司来做");
return result;
}
}

我只专注做结婚的事情

1
2
3
4
5
6
public class My implements ProxyInterface {
@Override
public void marry() {
System.out.println("我结婚啦〜");
}
}

主程序运行

1
2
3
4
5
6
7
8
9
public static void main (String[] args) {
WeddingCompany weddingCompany = new WeddingCompany(new My());
// 设置要代理的对象
weddingCompany.setTarget(my);

ProxyInterface proxyInterface =(ProxyInterface) weddingCompany.getProxy();
proxyInterface.marry();

}

实现3(cglib代理):

cglib动态代理最大的区别就是

  • 使用动态代理的对象必须实现一个或多个接口
  • 使用cglib代理的对象则无需实现接口,达到代理类无侵入。

使用cglib需要引入cglib的jar包,如果你已经有spring-core的jar包,则无需引入,因为spring中包含了cglib。

引入cglib依赖

1
2
3
4
5
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.5</version>
</dependency>

婚庆公司需要做的事

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class WeddingCompany implements MethodInterceptor{

private Object target;//维护一个目标对象
public ProxyFactory(Object target) {
this.target = target;
}

//为目标对象生成代理对象
public Object getProxyInstance() {
//工具类
Enhancer en = new Enhancer();
//设置父类
en.setSuperclass(target.getClass());
//设置回调函数
en.setCallback(this);
//创建子类对象代理
return en.create();
}

// 处理代理实例, 并返回结果
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 添加方法
System.out.println("我们是婚庆公司的");
System.out.println("我们在做结婚前的准备⼯作");
System.out.println("节⽬彩排...");
System.out.println("礼物购买...");
System.out.println("⼯作⼈员分⼯...");
System.out.println("可以开始结婚了");
Object result = method.invoke(proxyInterface, args);
System.out.println("结婚完毕,我们需要做后续处理,你们可以回家了,其余的事情我们公司来做");
return result;
}
}

我只专注做结婚的事情

1
2
3
4
5
public class My {
public void marry() {
System.out.println("我结婚啦〜");
}
}

主程序运行

1
2
3
4
5
6
7
8
9
10
11
public static void main (String[] args) {
//目标对象
My target = new My();
System.out.println(target.getClass());
//代理对象
My proxy = (My) new ProxyFactory(target).getProxyInstance();
System.out.println(proxy.getClass());
//执行代理对象方法
proxy.marry();

}

4. 观察者模式

该模式主要用于对象间存在一对多关系时,当一个对象被修改时,则会自动通知依赖它的对象(例如:拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价,即观察者)

实现:

创建拍卖师,用于观察价格变动以及通知其他竞价者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class Subject {

private List<Observer> observers
= new ArrayList<Observer>();
private int price;

public int getPrice() {
return price;
}

//设置价格
public void setPrice(int price) {
this.price = price;
//价格有变动,通知所有竞价者
notifyAllObservers();
}

//增加竞价者
public void attach(Observer observer){
observers.add(observer);
}

public void notifyAllObservers(){
for (Observer observer : observers) {
observer.receive();
}
}
}

创建观察者

1
2
3
4
public abstract class Observer {
protected Subject subject;
public abstract void receive();
}

创建实体观察者类,即竞价者1

1
2
3
4
5
6
7
8
9
10
11
12
public class BidderObserver1 extends Observer{

public BidderObserver1(Subject subject){
this.subject = subject;
this.subject.attach(this);
}

@Override
public void receive() {
System.out.println( "竞价者1收到价格变动: "+subject.getPrice() );
}
}

创建实体观察者类,即竞价者2

1
2
3
4
5
6
7
8
9
10
11
12
public class BidderObserver2 extends Observer{

public BidderObserver2(Subject subject){
this.subject = subject;
this.subject.attach(this);
}

@Override
public void receive() {
System.out.println( "竞价者2收到价格变动: "+subject.getPrice() );
}
}

主程序运行

1
2
3
4
5
6
7
8
9
10
11
12
public static void main (String[] args) {
Subject subject = new Subject();

new BidderObserver1(subject);
new BidderObserver2(subject);

System.out.println("First price change: 15");
subject.setPrice(15);
System.out.println("Second price change: 10");
subject.setPrice(10);

}

5. 工厂模式

定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。(例如:您需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现;需要画一个形状,可以直接从形状工厂里找一个形状,不用去关心它内部是怎么画出来的、用了哪些颜色以及技巧)

实现:

创建一个形状接口

1
2
3
public interface Shape {
void draw();
}

创建长方形的接口实现类

1
2
3
4
5
6
7
public class Rectangle implements Shape {

@Override
public void draw() {
System.out.println("内部绘制了一个长方形");
}
}

创建正方形的接口实现类

1
2
3
4
5
6
7
public class Square implements Shape {

@Override
public void draw() {
System.out.println("内部绘制了一个正方形");
}
}

创建圆形的接口实现类

1
2
3
4
5
6
7
public class Circle implements Shape {

@Override
public void draw() {
System.out.println("内部绘制了一个圆形");
}
}

创建一个工厂,生成基于给定信息的实体类的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ShapeFactory {

//使用 getShape 方法获取形状类型的对象
public Shape getShape(String shapeType){
if(shapeType == null){
return null;
}
switch (shapeType){
case "CIRCLE":
return new Circle();
case "RECTANGLE":
return new Rectangle();
case "SQUARE":
return new Square();
default:
throw new RuntimeException("无法找到形状");
}
}
}

主程序运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) {
ShapeFactory shapeFactory = new ShapeFactory();

//获取 Circle 的对象,并调用它的 draw 方法
Shape shape1 = shapeFactory.getShape("CIRCLE");
shape1.draw();

//获取 Rectangle 的对象,并调用它的 draw 方法
Shape shape2 = shapeFactory.getShape("RECTANGLE");
shape2.draw();

//获取 Square 的对象,并调用它的 draw 方法
Shape shape3 = shapeFactory.getShape("SQUARE");
shape3.draw();
}

6. 策略模式

在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改,可用于消除多重的if…else (例子:旅行的出游方式,选择骑自行车、坐汽车,每一种旅行方式都是一个策略)

实现:

创建策略接口,用于两数运算处理

1
2
3
public interface Strategy {
public int doOperation(int num1, int num2);
}

创建加、减、乘接口的实现类

1
2
3
4
5
6
public class OperationAdd implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 + num2;
}
}
1
2
3
4
5
6
public class OperationSubtract implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 - num2;
}
}
1
2
3
4
5
6
public class OperationMultiply implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 * num2;
}
}

创建Context类

1
2
3
4
5
6
7
8
9
10
11
public class Context {
private Strategy strategy;

public Context(Strategy strategy){
this.strategy = strategy;
}

public int executeStrategy(int num1, int num2){
return strategy.doOperation(num1, num2);
}
}

主程序运行

1
2
3
4
5
6
7
8
9
10
11
public static void main(String[] args) {

Context context = new Context(new OperationAdd());
System.out.println("10 + 5 = " + context.executeStrategy(10, 5));

context = new Context(new OperationSubtract());
System.out.println("10 - 5 = " + context.executeStrategy(10, 5));

context = new Context(new OperationMultiply());
System.out.println("10 * 5 = " + context.executeStrategy(10, 5));
}

7. 模板模式

在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板(注意:防止恶意操作,一般模板方法都加上 final 关键词)。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。(例子:spring 中对 Hibernate 的支持,将一些已经定好的方法封装起来,比如开启事务、获取 Session、关闭 Session 等)

实现:

创建一个抽象类,并将模板方法被设置为 final。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public abstract class Game {
abstract void initialize();
abstract void startPlay();
abstract void endPlay();

//模板
public final void play(){

//初始化游戏
initialize();

//开始游戏
startPlay();

//结束游戏
endPlay();
}
}

创建扩展了上述类的实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Basketball extends Game {

@Override
void endPlay() {
System.out.println("Basketball Game Finished!");
}

@Override
void initialize() {
System.out.println("Basketball Game Initialized! Start playing.");
}

@Override
void startPlay() {
System.out.println("Basketball Game Started. Enjoy the game!");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Football extends Game {

@Override
void endPlay() {
System.out.println("Football Game Finished!");
}

@Override
void initialize() {
System.out.println("Football Game Initialized! Start playing.");
}

@Override
void startPlay() {
System.out.println("Football Game Started. Enjoy the game!");
}
}

主程序运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void main(String[] args) {
Game game = new Cricket();
game.play();
System.out.println();
game = new Football();
game.play();
/**
Basketball Game Initialized! Start playing.
Basketball Game Started. Enjoy the game!
Basketball Game Finished!

Football Game Initialized! Start playing.
Football Game Started. Enjoy the game!
Football Game Finished!
*/
}

8. 适配器模式

适配器模式是将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作(例子:java的jdbc;音频播放器设备只能播放 mp3 文件,通过使用一个更高级的音频播放器来播放 vlc 和 mp4 文件并且还能兼容mp3文件的播放)

实现:

为媒体播放器和更高级的媒体播放器创建接口。

1
2
3
public interface MediaPlayer {
public void play(String audioType, String fileName);
}
1
2
3
4
public interface AdvancedMediaPlayer { 
public void playVlc(String fileName);
public void playMp4(String fileName);
}

创建实现了 AdvancedMediaPlayer 接口的实体类

1
2
3
4
5
6
7
8
9
10
11
public class VlcPlayer implements AdvancedMediaPlayer{
@Override
public void playVlc(String fileName) {
System.out.println("Playing vlc file. Name: "+ fileName);
}

@Override
public void playMp4(String fileName) {
//什么也不做
}
}
1
2
3
4
5
6
7
8
9
10
11
12
public class Mp4Player implements AdvancedMediaPlayer{

@Override
public void playVlc(String fileName) {
//什么也不做
}

@Override
public void playMp4(String fileName) {
System.out.println("Playing mp4 file. Name: "+ fileName);
}
}

创建实现了 MediaPlayer 接口的适配器类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class MediaAdapter implements MediaPlayer {

AdvancedMediaPlayer advancedMusicPlayer;

public MediaAdapter(String audioType){
if(audioType.equalsIgnoreCase("vlc") ){
advancedMusicPlayer = new VlcPlayer();
} else if (audioType.equalsIgnoreCase("mp4")){
advancedMusicPlayer = new Mp4Player();
}
}

@Override
public void play(String audioType, String fileName) {
if(audioType.equalsIgnoreCase("vlc")){
advancedMusicPlayer.playVlc(fileName);
}else if(audioType.equalsIgnoreCase("mp4")){
advancedMusicPlayer.playMp4(fileName);
}
}
}

创建实现了 MediaPlayer 接口的实体类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class AudioPlayer implements MediaPlayer {
MediaAdapter mediaAdapter;

@Override
public void play(String audioType, String fileName) {

//播放 mp3 音乐文件的内置支持
if(audioType.equalsIgnoreCase("mp3")){
System.out.println("Playing mp3 file. Name: "+ fileName);
}
//mediaAdapter 提供了播放其他文件格式的支持
else if(audioType.equalsIgnoreCase("vlc")
|| audioType.equalsIgnoreCase("mp4")){
mediaAdapter = new MediaAdapter(audioType);
mediaAdapter.play(audioType, fileName);
}
else{
System.out.println("Invalid media. "+
audioType + " format not supported");
}
}
}

主程序运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void main(String[] args) {
AudioPlayer audioPlayer = new AudioPlayer();

audioPlayer.play("mp3", "beyond the horizon.mp3");
audioPlayer.play("mp4", "alone.mp4");
audioPlayer.play("vlc", "far far away.vlc");
audioPlayer.play("avi", "mind me.avi");
/**
Playing mp3 file. Name: beyond the horizon.mp3
Playing mp4 file. Name: alone.mp4
Playing vlc file. Name: far far away.vlc
Invalid media. avi format not supported
*/
}

9. 装饰器模式

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装,可以动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活

实现:

创建一个接口

1
2
3
public interface Shape {
void draw();
}

创建实现接口的实体类

1
2
3
4
5
6
7
public class Rectangle implements Shape {

@Override
public void draw() {
System.out.println("Shape: Rectangle");
}
}
1
2
3
4
5
6
7
public class Circle implements Shape {

@Override
public void draw() {
System.out.println("Shape: Circle");
}
}

创建实现了 Shape 接口的抽象装饰类

1
2
3
4
5
6
7
8
9
10
11
public abstract class ShapeDecorator implements Shape {
protected Shape decoratedShape;

public ShapeDecorator(Shape decoratedShape){
this.decoratedShape = decoratedShape;
}

public void draw(){
decoratedShape.draw();
}
}

创建扩展了 ShapeDecorator 类的实体装饰类,新增边框上色功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class RedShapeDecorator extends ShapeDecorator {

public RedShapeDecorator(Shape decoratedShape) {
super(decoratedShape);
}

@Override
public void draw() {
decoratedShape.draw();
setRedBorder(decoratedShape);
}

private void setRedBorder(Shape decoratedShape){
System.out.println("Border Color: Red");
}
}

主程序运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public static void main(String[] args) {
Shape circle = new Circle();
ShapeDecorator redCircle = new RedShapeDecorator(new Circle());
ShapeDecorator redRectangle = new RedShapeDecorator(new Rectangle());
//Shape redCircle = new RedShapeDecorator(new Circle());
//Shape redRectangle = new RedShapeDecorator(new Rectangle());
System.out.println("Circle with normal border");
circle.draw();

System.out.println("\nCircle of red border");
redCircle.draw();

System.out.println("\nRectangle of red border");
redRectangle.draw();
/**
Circle with normal border
Shape: Circle

Circle of red border
Shape: Circle
Border Color: Red

Rectangle of red border
Shape: Rectangle
Border Color: Red
*/
}

10. 外观模式

外观模式(Facade Pattern)隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性(例子:去医院看病,可能要去挂号、门诊、划价、取药,让患者或患者家属觉得很复杂,如果有提供接待人员,只让接待人员来处理,就很方便)

实现:

创建一个接口

1
2
3
public interface Shape {
void draw();
}

创建实现接口的实体类

1
2
3
4
5
6
7
public class Rectangle implements Shape {

@Override
public void draw() {
System.out.println("Shape: Rectangle");
}
}
1
2
3
4
5
6
7
public class Circle implements Shape {

@Override
public void draw() {
System.out.println("Shape: Circle");
}
}

创建一个外观类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ShapeMaker {
private Shape circle;
private Shape rectangle;

public ShapeMaker() {
circle = new Circle();
rectangle = new Rectangle();
}

public void drawCircle(){
circle.draw();
}
public void drawRectangle(){
rectangle.draw();
}
}

主程序运行

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
ShapeMaker shapeMaker = new ShapeMaker();

shapeMaker.drawCircle();
shapeMaker.drawRectangle();
/**
Circle::draw()
Rectangle::draw()
*/
}