No2.大话设计模式学习之工厂方法、建造者、观察者模式
2.设计模式-part2
-
2.4 工厂方法模式
collapsed:: true- 描述:
工厂方法模式(Factory Method Pattern)
是指定义一个用于创建对象的结构,让子类决定实例化哪个类,即类的实例化过程延迟到其子类 - 核心点:客户端决定实例化哪一个工厂来实现功能类,即将[[简单工厂]]内部的逻辑判断移到客户端进行
- 原则1:有明确的根据不同条件创造实例的计划时。将类类比为产品,产品具有系列/组合的形式,则使用者可以在不清楚类生产的具体过程及一个系列/组合的类包含的具体内容的情况下,使用一个系列的产品
- 原则2:无法预知对象确切类别及其依赖关系时,工厂方法能将创建产品的代码与实际使用铲平的代码分离,从而能在不影响其他代码的情况下扩展产品创建的部分。
- 原则3:希望用户能扩展软件库/框架的内部组件
- 好处1:可以避免创建者和具体产品之间的紧密耦合;
- 好处2:扩展性高,如果想增加一个新的产品,只需要扩展一个工厂类就可以;
- 好处3:符合“开放封闭原则”,无需更改现有工厂类代码,就可以引入新的功能;
- 好处4:符合“单一职责原则”,可以将产品创建代码放在程序的单一位置,从而使得代码更容易维护。
- 坏处1:代码可能变得复杂,因为需要引入许多类
- eg:(大学生薛磊风在过去三年里一直在帮助孤寡老人,每周都去老人家里,为老人洗衣扫地、买米买油。有一次,他不幸受了伤,便委托他的两个同学继续去帮助老人,且不必提及任何人的名字,只需说是学雷锋做好事即可
帮助老人是长期工作,三名“学雷锋的大学生”毕业后,也依然会以“社区志愿者”的名义继续学雷锋做好事。而老人其实不需要知道是谁来做好事,只需要知道是学雷锋的人来帮助就可以了)- 场景:先用
简单工厂模式
实现,但由于学雷锋的学生毕业后,会转变身份成为社区志愿者,此时若使用简单工厂模式会涉及到工厂类的修改,违背开放封闭原则。当我们使用工厂方法模式
时,可以对学雷锋的学生和社区志愿者分别建立工厂类,让客户端决定实例化哪一个工厂类 - 步骤:
- 创建抽象类
LetFeng
,同时定义公共接口,即三种好事: - 定义方法
Sweep()
,Wash()
,BuyRice()
。 - 创建具体的做好事的类:学雷锋的大学生
Undergraduate
,及社区志愿者Volunteer
,继承于抽象类LetFeng
; - 创建雷锋工厂类
IFactory
,再定义学雷锋的大学生工厂UndergraduateFactory
和社区志愿者工厂VolunteerFactory
继承于雷锋工厂,用于创建具体的对象。
- 创建抽象类
- 代码实现:
- 首先创建抽象类
LetFeng
-
public class LeiFeng { public void sweep() { System.out.println("Sweep"); } public void wash() { System.out.println("Wash"); } public void buyRice() { System.out.println("Buy rice"); } }
- 创建做好事的类,学雷锋的大学生
Undergraduate
,及社区志愿者Volunteer
-
public class Undergraduate extends LeiFeng { } public class Volunteer extends LeiFeng { }
- 创建雷锋工厂类
IFactory
,再定义学雷锋的大学生工厂UndergraduateFactory
和社区志愿者工厂VolunteerFactory
-
public interface IFactory { public LeiFeng createLeiFeng(); } public class UndergraduateFactory implements IFactory { @Override public LeiFeng createLeiFeng() { return new Undergraduate(); } } public class VolunteerFactory implements IFactory { @Override public LeiFeng createLeiFeng() { return new Volunteer(); } }
- 调用方
-
public class FactoryMethodClient { public static void main(String[] args) { IFactory factory = new UndergraduateFactory(); LeiFeng student = factory.createLeiFeng(); student.buyRice(); student.sweep(); student.wash(); LeiFeng volunteer = new VolunteerFactory().createLeiFeng(); volunteer.buyRice(); volunteer.sweep(); volunteer.wash(); } }
- 首先创建抽象类
- 工厂方法模式通用结构示意图如下(引申)
- 场景:先用
- 描述:
-
2.5 建造者模式
- 描述:建造者模式(Builder)复杂对象的构建与它的表现分离,使得同样的构建过程可以创建不同的
- 区别:与工厂模式相比,建造者模式更关注与零件装配的顺序
- 原则1:高层次的类不应依赖低层次的类,都应依赖于抽象接口
- 原则2:抽象不应该依赖细节,细节应该依赖抽象
- 原则3:适合同类算法较多的场景,较少不太适合
- 好处1:使得建造代码与表示代码分离,易扩展
- 好处2:便于控制细节风险
- 坏处1:产品必须有共同点,范围有限制
- 坏处2:如内部变化复杂,会有很多的建造类
- eg:(创建一些复杂的对象,这些对象内部构建间的建造顺序通常是稳定的,但对象内部的构建通常面临着复杂的变化;一些基础部件不变,而其组合经常变化)
- 场景:构造小人的过程是稳定的,都需要头身手脚,具体建造的"细节"不同,有高矮胖瘦
- 步骤:
- 先定义一个抽象的建造人的类,把这个过程稳定住,里面有构建头身手脚的抽象方法;
- 然后建造具体的小人,去继承上面的抽象类;
- 创建一个指挥者,用它来控制构建过程,隔离用户与构造过程的关联
- 代码实现:
PersonBuilder
抽象类-
import java.awt.*; public abstract class PersonBuilder { protected Graphics2D g; protected Pen p; public PersonBuilder(Graphics2D g, Pen p) { g.setStroke(new BasicStroke(p.getLineWidth())); g.setColor(p.getColor()); this.g = g; this.p = p; } public abstract void buildHead(); public abstract void buildBody(); public abstract void buildArmLeft(); public abstract void buildArmRight(); public abstract void buildLegLeft(); public abstract void buildLegRight(); }
PersonFatBuilder
具体类-
import java.awt.*; public class PersonFatBuilder extends PersonBuilder { public PersonFatBuilder(Graphics2D g, Pen p) { super(g, p); } @Override public void buildHead() { g.drawOval(200, 70, 30, 30); } @Override public void buildBody() { g.drawRect(185, 100, 60, 30); } @Override public void buildArmLeft() { g.drawLine(205, 100, 175, 110); } @Override public void buildArmRight() { g.drawLine(225, 100, 250, 110); } @Override public void buildLegLeft() { g.drawLine(205, 130, 175, 150); } @Override public void buildLegRight() { g.drawLine(225, 130, 250, 150); } }
PersonThinBuilder
具体类-
import java.awt.*; public class PersonThinBuilder extends PersonBuilder { public PersonThinBuilder(Graphics2D g, Pen p) { super(g, p); } @Override public void buildHead() { g.drawOval(50, 20, 30, 30); } @Override public void buildBody() { g.drawRect(60, 50, 10, 50); } @Override public void buildArmLeft() { g.drawLine(60, 50, 40, 100); } @Override public void buildArmRight() { g.drawLine(70, 50, 90, 100); } @Override public void buildLegLeft() { g.drawLine(60, 100, 45, 150); } @Override public void buildLegRight() { g.drawLine(70, 100, 85, 150); } }
PersonDirector
类-
public class PersonDirector { private PersonBuilder pb; public PersonDirector(PersonBuilder pb) { this.pb = pb; } public void createPerson() { pb.buildHead(); pb.buildBody(); pb.buildArmLeft(); pb.buildArmRight(); pb.buildLegLeft(); pb.buildLegRight(); } }
Pen
类-
import java.awt.*; public class Pen { private int lineWidth; private Color color; public Pen(int lineWidth, Color color) { this.lineWidth = lineWidth; this.color = color; } public int getLineWidth(){ return lineWidth; } public Color getColor(){ return color; } }
BuilderMain
方法-
import javax.swing.*; import java.awt.*; public class BuilderMain { public static void main(String[] args) { JFrame jFrame = new JFrame(); JPanel jpanel = new JPanel() { @Override public void paint(Graphics graphics) { super.paint(graphics); PersonBuilder ptb = new PersonThinBuilder((Graphics2D) graphics, new Pen(2, Color.BLUE)); PersonDirector pd = new PersonDirector(ptb); pd.createPerson(); PersonBuilder pfb = new PersonFatBuilder((Graphics2D) graphics, new Pen(3, Color.YELLOW)); pd = new PersonDirector(pfb); pd.createPerson(); } }; jFrame.add(jpanel); jFrame.setSize(300, 300); jFrame.setVisible(true); jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } }
- 建造者模式通用结构示意图如下(引申)
-
2.6 观察者模式又叫发布-订阅模式
- 描述:观察者模式又叫发布-订阅(Publish/Subscribe)模式,描述了单个对象和一个或多个对象之间的发布-订阅关系
- 原则1:当一个对象的改变需要同时改变多个(不知道具体数目)对象时。
- 原则2:当一个抽象模型的两个方面一方依赖于另一方时,将两者封装在独立的对象中
- 好处1:Subject 和 Observer 之间松耦合,可以轻松扩展。而且两者都可以扩展,不会对系统造成影响。
- 好处2:支持广播通信,不需要指定接收者,而且可以随时增删 Observer
- 坏处1:不同的订阅者可能需要不同的更新(功能),而不是所有的都一样。比如炒股的可能要切换桌面,而看 NBA 的既要切换桌面还得关掉声音。
- 坏处2:Subject 依赖于 Observer 对抽象接口的实现,没有实现就无法更新。比如炒股的没有实现更新方法,那他的摸鱼行为自然就要暴露了
- eg:(推模型:Subject 起主导作用,向 Observer 推送。拉模型:Subject 对变化进行广播,由 Observer 负责拉取更新)
- 场景:针对领导的群就是 Subject,员工则是 Observer,一个 Subject 可以有多个 Observer,它不需要关心到底有哪些 Observer,Observer 之间也不需要知道彼此存在。当 Subject 的状态发生变化(即领导回来)时,所有的 Observer 都会得到通知,并更新自己的行为(努力工作)。
- 当然,反过来一个 Observer 可以订阅多个 Subject,任意一个 Subject 的状态发生变化,该 Observer 都会得到通知。这样就既解决了一致性问题,又不会过紧耦合。
- 步骤:
- 主题抽象类
Subject
,定义attach
、detach
和inform
方法。 - 观察者抽象类
Observer
,定义update
方法。 - 具体主题类
Boss
。 - 具体观察者类
NBAObserver
,StockObserver
等,每个类重写自己的update
方法
- 主题抽象类
- 代码实现:
Subject
类-
public interface Subject { void attach(Observer observer); void detach(Observer observer); void inform(); String getSubjectState(); void setSubjectState(String action); }
Observer
类-
public abstract class Observer { protected String name; protected Subject sub; public Observer(String name, Subject sub) { this.name = name; this.sub = sub; } public abstract void update(); }
ConcreteSubject
类-
public class Boss implements Subject { private List<Observer> observers = new ArrayList<>(); private String action; @Override public void attach(Observer observer) { observers.add(observer); } @Override public void detach(Observer observer) { observers.remove(observer); } @Override public void inform() { for(Observer o : observers){ o.update(); } } @Override public String getSubjectState() { return action; } @Override public void setSubjectState(String action) { this.action = action; } }
ConcreteObserver
类-
public class NBAObserver extends Observer { public NBAObserver(String name, Subject sub) { super(name, sub); } @Override public void update() { System.out.println(sub.getSubjectState() + " " + name + " 关闭NBA直播,继续工作!"); } } public class StockObserver extends Observer { public StockObserver(String name, Subject sub) { super(name, sub); } @Override public void update() { System.out.println(sub.getSubjectState() + " " + name + " 关闭股票行情,继续工作!"); } }
- 调用方
-
public class Main { public static void main(String[] args) { Boss huhansan = new Boss(); Observer tongshi1 = new StockObserver("魏关姹", huhansan); Observer tongshi2 = new NBAObserver("易管查", huhansan); huhansan.attach(tongshi1); huhansan.attach(tongshi2); huhansan.detach(tongshi1); huhansan.setSubjectState("我胡汉三回来了!"); huhansan.inform(); } }
- 观察者模式通用结构示意图如下(引申)
- 参考资料:《深入设计模式》