希望通过本书的阅读,掌握一些设计模式思维,在以后的编码当中逐渐体会。
目录
1.欢迎来到设计模式世界,设计模式入门
1.超类里放的一定要是完全公共的东西。
2.策略模式的概念:策略模式定义了算法族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。
3.设计模式使你和其它工程师有了很多共享词汇,交流更加方便。
4.不是仅仅知道设计模式的概念,就可以设计出弹性,可复用,可维护的系统,事实证明,只有通过不断的艰苦实践,才能成功。
设计原则1:找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
设计原则2:针对接口编程,而不是针对实现编程。
设计原则3:多用组合,少用继承。
5.策略模式的类图:

6.策略模式的测试代码:
首先是抽象类与它的继承类
public abstract class Duck {
//不断变化的元素
FlyBehavior flyBehavior;
//公共不变的元素
public void swim() {
System.out.println("swim");
}
//flyBehavior的类型在子类中定义,利用多态,十分方便
public void performFly() {
flyBehavior.fly();
}
//运行中动态改变不断变化的元素
public void setFlyBehavior(FlyBehavior fb) {
flyBehavior = fb;
}
}
public class ModelDuck extends Duck{
public ModelDuck() {
flyBehavior = new FlyWithWings();
}
}
不变的元素直接写在了抽象类中,变化的元素,用接口进行扩展
public interface FlyBehavior {
public void fly();
}
public class FlyWithWings implements FlyBehavior{
public void fly() {
System.out.println("wings");
}
}
public class FlyRocketPowers implements FlyBehavior{
public void fly() {
System.out.println("rockets");
}
}
测试类
public class Test {
public static void main(String[] args) {
Duck model = new ModelDuck();
model.performFly();
//可以运行中动态设置变化的部分
model.setFlyBehavior(new FlyRocketPowers());
model.performFly();
}
}
2.让你的对象知晓现况:观察者模式
设计原则4:为了交互对象之间的松耦合设计而努力。
1.改变的地方需要封装起来。
2.观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖着都会收到通知并自动更新。
3.当两个对象之间松耦合,他们依然可以交互,但是不太清楚彼此的细节,将相互依赖降到了最低。
4.观察者模式提供了一种对象设计,让主题和观察者之间松耦合。
5.观察者模式可以采用推和拉两种方式。
6.java.util.Observable可能符合需求,但是不符合一些良好的设计原则。
7.观察者模式的类图:

8.观察者模式的测试代码(推的方式):
首先是主题和观察者接口
public interface Subject {
//主题接口
//添加观察者
public void registerObserver(Observer o);
//删除观察者
public void removeObserver(Observer o);
//通知观察者
public void notifyObservers();
}
public interface Observer {
//观察者接口
public void update(float temp, float humidity, float pressure);
}
public interface DisplayElement {
//显示功能的接口,为了测试而加入,非必要的部分
public void display();
}
然后是主题的实现类
package priv;
import java.util.ArrayList;
public class WeatherData implements Subject{
//用一个容器来保存观察者们
private ArrayList<Observer> observers;
private float temperature;
private float humidity;
private float pressure;
private boolean changed = false;
public WeatherData(){
observers = new ArrayList<Observer>();
}
public void registerObserver(Observer o) {
observers.add(o);
}
public void removeObserver(Observer o) {
int i = observers.indexOf(o);
if (i >= 0) {
observers.remove(i);
}
}
public void notifyObservers() {
if(changed) {
for(int i = 0; i <observers.size(); i++) {
Observer observer = observers.get(i);
observer.update(temperature, humidity, pressure);
}
}
}
//这个方法使得更新观察者更有弹性,可以设置一些调节,比如变化达到多少的时候才进行通知
public void setChanged() {
changed = true;
}
public void measurementsChanged() {
setChanged();
notifyObservers();
}
public void setMeasureMents(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
}
观察者实现类:
package priv;
public class CurrentConditionDisplay implements Observer, DisplayElement{
private float temperature;
private float humidity;
private float pressure;
private Subject weatherData;
public CurrentConditionDisplay(Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
display();
}
public void display() {
System.out.println("当前的温度是"+temperature+humidity+pressure);
}
}
9.拉的方式非常简单,主题不再调用update方法,而是在主题中加一些get方法,当观察者有需要的时候自己过来拿。
3.装饰对象:装饰者模式
设计原则5:类应该对扩展开放,对修改关闭。
1.不用每一个部分都采用开放闭合原则,这可能是一种浪费,而且会增加复杂度。你需要把注意力集中在最容易改变的部分,然后使用开放闭合原则。
public class DecoratorTest {
public static void main(String[] args)
{
//如果不用装饰者模式,那么就得一个继承一个
//那么现在呢,都继承的式food,避免了臃肿的继承关系,多继承,继承复杂的问题
//在这个装饰者下就可以随意的组合了
//主要用到的知识点就是多态
//实现主要就是带一个有参的构造方法,将父类传进去
Meat meat=new Meat(new Milk(new Egg(new Food())));
meat.make();
}
public static class Food
{
public void make()
{
System.out.print("面包");
}
}
public static class Egg extends Food{
private Food basicFood;
public Egg(Food basicFood)
{
this.basicFood=basicFood;
}
public void make() {
basicFood.make();
System.out.print("+鸡蛋");
}
}
public static class Milk extends Food{
private Food basicFood;
public Milk(Food basicFood)
{
this.basicFood=basicFood;
}
public void make() {
basicFood.make();
System.out.print("+牛奶");
}
}
public static class Meat extends Food{
private Food basicFood;
public Meat(Food basicFood)
{
this.basicFood=basicFood;
}
public void make() {
basicFood.make();
System.out.print("+肉");
}
}
}
4.烘烤OO的精华:工厂模式
1.工厂方法模式定义了一个创建对象的类,但由子类决定要实例化的类是哪一个,工厂方法让类把实例化推迟到子类。
类图

具体实现代码:
先来创建比萨以及它的子类
package priv;
public abstract class Pizza {
//每个披萨都有他自己的名称,面团类型以及加入的作料
String name;
String dough;
String sauce;
void prepare() {
System.out.println(name);
System.out.println(dough);
System.out.println(sauce);
}
void bake() {
System.out.println("bake");
}
void cut() {
System.out.println("cut");
}
void box() {
System.out.println("box");
}
public String getName() {
return name;
}
}
package priv;
public class NYStyleCheesePizza extends Pizza{
public NYStyleCheesePizza() {
name = "NYStyleCheesePizza";
dough = "NYStyleCheesePizza";
sauce = "NYStyleCheesePizza";
}
}
package priv;
public class ChicagoStyleCheesePizza extends Pizza{
public ChicagoStyleCheesePizza() {
name = "ChicagoStyleCheesePizza";
dough = "ChicagoStyleCheesePizza";
sauce = "ChicagoStyleCheesePizza";
}
}
然后来创建pizza工厂类
package priv;
public abstract class PizzaStore {
public Pizza orderPizza(String type) {
Pizza pizza;
pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
//创建具体披萨的操作交给实现的子类
protected abstract Pizza createPizza(String type);
}
package priv;
public class NYPizzaStore extends PizzaStore{
protected Pizza createPizza(String type) {
if(type.equals("cheese")) {
return new NYStyleCheesePizza();
}else {
//省略了过多的代码,也就是其他类型的披萨
return new NYStyleCheesePizza();
}
}
}
2.简单工厂就是将创建的部分提取出来,但是不具备工厂方法的弹性。
设计原则6:依赖倒置原则,要依赖抽象,而不是依赖具体的类。不能让高层组件依赖于底层组件,而且,不管高层或底层组件,两者都应该依赖于抽象。


3.三点依赖倒置原则指导方针:
(1)变量不可以持有具体类的引用。也就是尽量用工厂代替new。
(2)不要让类派生自具体类。要派生自抽象。
(3)不要覆盖基类中已经实现的方法。
尽量而非必须遵守,如果有一个不会改变的类,那么直接实例化也没有什么大碍。
4.抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
我们只需要对工厂方法模式代码中的prepare方法创建原料进行改造,即可以利用抽象工厂模式,其类图如下:

5.工厂方法与抽象工厂的区别:
(1)工厂方法用的是继承,而抽象工厂用的是对象的组合。
(2)工厂方法可以把客户代码从实例化的具体类中解耦,只需把它继承成子类,并实现它 的工厂方法就可以了。而抽象工厂,应用于创建产品家族和想让制造的相关产品集合起来。
5.独一无二的对象:单件模式(单例模式)
1.一个对象的常用场景:线程池,缓存,对话框,日志对象等。
2.单件模式,确保一个类只有一个实例,并提供一个全局访问点。
3.懒汉原始式:在反射面前没什么用,线程不安全
需要注意的式构造方法也是private
public class Singleton {
private Singleton() {}
private static Singleton singleton = null;
public static Singleton getInstance() {
if (singleton== null) {
singleton= new Singleton();
}
return singleton;
}
}
产生不安全的原因:
线程一 线程二
if (singleton== null) {
if (singleton== null) {
singleton= new Singleton();
return singleton;
singleton= new Singleton();
return singleton;
两个线程独立,同时判断条件,都满足就都进入到了创建对象的环节。
4.懒汉线程同步式:在方法调用上加了同步,虽然线程安全了,但是每次都有同步,会影响性能,毕竟99%是不需要同步的 ,只有第一次调用的时候才会真正利用同步,以后对象都不是null的,就直接return了,所以十分浪费资源。
public class Singleton {
private Singleton() {}
private static Singleton singleton = null;
//为了保证多线程环境下正确访问,给方法加上同步锁synchronized
public static synchronized Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
5.懒汉式双重校验锁:线程安全,且确保了只有第一次调用单例时才会做同步,避免了每次都同步的性能损耗;同样的,也降低了性能。
public class Singleton {
private Singleton() {}
private volatile static Singleton singleton = null;
//双重锁检查
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
//创建对象的操作不是原子的,分为三个步骤,volatile防止了多线程的重排序
singleton = new Singleton();
}
}
}
return singleton;
}
}
双重检查校验锁不适用于1.4以及更早的java版本
6.
饿汉式:类初始化的时候就立刻实例化,天生线程安全,系统启动时,占用资源;如果后期这个单例没有被使用,会造成资源浪费
注意这里面创建的时候有关键字final
public class Singleton {
private Singleton() {}
private static final Singleton singleton = new Singleton();
public static Singleton getInstance() {
return singleton;
}
}
6.封装调用:命令模式
1.命令模式将请求封装成对象,以便使用不同的请求,队列或者日志来参数化其它对象,命令模式也支持可撤销的操作。
2.命令模式将发出的请求对象和执行请求的对象解耦。
3.在被解耦的两者之间是通过命令对象进行沟通的,命令对象封装了接受者的一组动作。
4.命令可以撤销。
5.可以同时调用一组命令。
6.命令模式的类图:

7.命令模式的代码:
首先是命令
package priv;
public interface Command {
public void execute();
public void undo();
}
package priv;
public class LightOnCommand implements Command{
Light light;
public LightOnCommand(Light light) {
this.light = light;
}
public void execute() {
light.on();
}
public void undo() {
light.off();
}
}
package priv;
public class LightOffCommand implements Command{
Light light;
public LightOffCommand(Light light) {
this.light = light;
}
public void execute() {
light.off();
}
public void undo() {
light.on();
}
}
package priv;
public class NoCommand implements Command{
public void execute() {
}
public void undo() {
}
}
然后是各个独特的设备,卧室的灯,客厅的灯
package priv;
public class Light {
String name;
public Light(String string) {
this.name = string;
}
public void on() {
System.out.println("on");
}
public void off() {
System.out.println("off");
}
}
然后是远程控制遥控器
package priv;
public class RemoteControl {
//用一个数组表示不同的按钮
Command[] onCommands;
Command[] offCommands;
Command undoCommand;
public RemoteControl() {
onCommands = new Command[7];
offCommands = new Command[7];
Command noCommand = new NoCommand();
for(int i=0;i<7;i++) {
onCommands[i] = noCommand;
offCommands[i] = noCommand;
}
undoCommand = noCommand;
}
//将命令设置到远程控制的遥控器当中
public void setCommand(int slot, Command onCommand, Command offCommand) {
onCommands[slot] = onCommand;
offCommands[slot] = offCommand;
}
//按下相应的开关,并记录上一步的操作
public void onButtonWasPushed(int slot) {
onCommands[slot].execute();
undoCommand = onCommands[slot];
}
public void offButtonWasPushed(int slot) {
offCommands[slot].execute();
undoCommand = offCommands[slot];
}
//撤销到上一步操作
public void undoButtonWasPushed() {
undoCommand.undo();
}
}
然后进行测试:
package priv;
public class RemoteLoader {
public static void main(String[] args) {
//创建远程控制要控器
RemoteControl remoteControl = new RemoteControl();
//创建各自独特的设备
Light livingRoomLight = new Light("living room");
//创建命令
LightOnCommand lightOnCommand = new LightOnCommand(livingRoomLight);
LightOffCommand lightOffCommand = new LightOffCommand(livingRoomLight);
//将命令与要控器各个按钮进行绑定
remoteControl.setCommand(0, lightOnCommand, lightOffCommand);
//绑定完了,实现了远程控制遥控器和各个独特设备之间的解耦
remoteControl.onButtonWasPushed(0);
remoteControl.offButtonWasPushed(0);
remoteControl.undoButtonWasPushed();
}
}
7.随遇而安:适配器与外观模式
1.采用适配器模式,如果要实现一个很大的目标接口,,就有很大的工作要做,总比改写客户端的代码要好得多。
2.适配器也可以双向适配。
3.适配器模式将一个类的接口,转换成客户期望的另一个接口,适配器让原本接口不兼容的类可以合作无间。
4.外观模式改变接口的原因是为了简化接口,将复杂的东西隐藏在背后,只露出一个干净美好的外观。在一个方法内完成复杂子系统的调用过程。而且将客户从组件的子系统中解耦。
5.外观模式的定义:外观模式提供了一个统一的接口,用来访问子系统中的一群接口,外观定义了一个高层接口,让子系统更加容易使用。
6.装饰者与适配器与外观模式:
装饰者:不改变接口但假如责任。
适配器:将一个接口转成另一个接口。
外观:让接口更简单。
7.适配器模式类图:

设计原则7:最少知识原则,只和你的密友谈话,不要让太多的类耦合在一起,免得修改一部分会影响到其他部分。
例如:Thermometer thermometer = station.getThermometer();
return thermometer.getTemperature();
return station.getTemperature();在station里直接创建返回的方法,让我们少认识了一个Thermometer类
8.封装算法:模板方法模式
1.模板方法模式在一个方法中定义了一个算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中某些步骤。
2.模板方法的代码:
public abstract class Caffeine{
//定义了算法模板,声明为final,子类不可以改变
final void templateMethod(){
boilWater();
brew();
pourInCup();
if(hook()){
addCondiments();
}
}
//变化的部分,留着子类去实现
abstract void brew();
abstract void addCondiments()
void boilWater(){
//实现
}
void pourInCup(){
//实现
}
//钩子,可以控制算法的某些流程
boolean hook(){
return true;
}
}
3.当创建一个模板方法时,怎么才能知道什么时候使用抽象方法,什么时候使用钩子呢?
当你的子类必须提供算法中某个方法或者步骤的实现时,就使用抽象方法。如果算法的这个部分是可选的,就用钩子。
设计原则8:好莱坞原则,别调用我们,我们会调用你。我们允许低层组件将自己挂钩到系统上,高层组件对待底层组件的方式是别调用我们,我们会调用你。
4.模板方法模式与策略模式:
策略模式和模板方法模式都封装算法,然而,策略模式用的是组合,模板方法用的是继承,更加倾向于算法大纲。
9.管理良好的集合:迭代器与组合模式
1.迭代器模式提供了一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。优点:解耦,增强扩展性。
2.如果有一个统一的方法访问聚合中的每一个对象,你就可以编写多态的代码和这些聚合搭配。
3.迭代器类图

设计原则9:单一职责,一个类应该只有一个引起变化额原因。类的每个责任都有改变的潜在区域,超过一个责任,意味着超过一个改变的区域。
4.组合模式允许你将对象组合成树形结构来表现整体部分的层次结构,组合能让客户以一致的方式处理个别对象以及对象组合。
5.组合模式类图:

6.组合模式代码:
package priv;
public abstract class MenuComponent {
//这种方式在原码中经常可以看到,之前还不知道什么意思,怎么每个方法都抛出异常,原来是组合模式,实现都在他的组件里
//新增的方法
public void add(MenuComponent menuComponent) {
throw new UnsupportedOperationException();
}
public void remove(MenuComponent menuComponent) {
throw new UnsupportedOperationException();
}
public MenuComponent getChild(int i) {
throw new UnsupportedOperationException();
}
//各自的方法以及共有的方法,需要就进行重写
public String getName() {
throw new UnsupportedOperationException();
}
public String getDescription() {
throw new UnsupportedOperationException();
}
public double getPrice() {
throw new UnsupportedOperationException();
}
public boolean isVegetarian() {
throw new UnsupportedOperationException();
}
public void print() {
throw new UnsupportedOperationException();
}
}
package priv;
public class MenuItem extends MenuComponent{
String name;
String description;
boolean vegetarian;
double price;
public MenuItem(String name, String description, boolean vegetarian, double price) {
this.name = name;
this.description = description;
this.vegetarian = vegetarian;
this.price = price;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public double getPrice() {
return price;
}
public boolean isVegetarian() {
return vegetarian;
}
public void print() {
System.out.print(" "+getName());
if(isVegetarian()) {
System.out.print("v");
}
System.out.print(" "+getPrice());
System.out.print(" "+getDescription());
}
}
package priv;
import java.util.ArrayList;
import java.util.Iterator;
public class Menu extends MenuComponent{
ArrayList<MenuComponent> menuComponents = new ArrayList<MenuComponent>();
String name;
String description;
public Menu(String name, String description) {
this.name = name;
this.description = description;
}
public void add(MenuComponent menuComponent) {
menuComponents.add(menuComponent);
}
public void remove(MenuComponent menuComponent) {
menuComponents.remove(menuComponent);
}
public MenuComponent getChild(int i) {
return (MenuComponent)menuComponents.get(i);
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
//这里的组合递归实在是太秀了
//这里是核心
//由此递归创建了菜谱这样的树形结构
public void print() {
System.out.print(" "+getName());
System.out.print(" "+getDescription());
System.out.print("++++++++");
Iterator<MenuComponent> iterator = menuComponents.iterator();
while(iterator.hasNext()) {
MenuComponent menuComponent = (MenuComponent)iterator.next();
menuComponent.print();
}
}
}
package priv;
public class Waitress {
MenuComponent allMenus;
public Waitress(MenuComponent menuComponent) {
this.allMenus = menuComponent;
}
public void printMenu() {
allMenus.print();
}
}
10事物的状态:状态模式
1.状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
2.状态模式和策略模式的区别:
类图是一样的,但是他们的意图是不一样的。
状态模式:我们将一群行为封装在状态对象中,context的行为随时可以委托到那些状态对象中的一个。随着时间的流逝,当前状态在状态对象集合中游走变化,以反映context内部的状态,因此context的行为也会跟着变化,但是context 的客户对于状态对象了解不多,甚至浑然不觉。
策略模式:客户通常主动指定context所要组合的策略对象是哪一个。通常只有一个最适当的策略对象。
3.状态模式的类图:

4.状态模式的代码:(如果需要拓展,加一个新的状态的话,会变得特别容易)
package priv;
public class GumballMachine {
//两个状态类
State state1;
State state2;
//当前的状态
State state = state1;
//表示剩余数量什么的
int count = 0;
//初始化
public GumballMachine(int count) {
state1 = new State1();
state2 = new State2();
if(count>0) {
state = state2;
}
}
//将具体的实现委托到状态类当中
public void insert() {
state.insert();
}
public void eject() {
state.eject();
}
}
package priv;
public interface State {
public void insert();
public void eject();
}
package priv;
public class State1 implements State{
public void insert() {
// TODO Auto-generated method stub
}
public void eject() {
// TODO Auto-generated method stub
}
}
package priv;
public class State2 implements State{
public void insert() {
// TODO Auto-generated method stub
}
public void eject() {
// TODO Auto-generated method stub
}
}
11控制对象访问:代理模式
1.代理模式为另一个对象提供了一个替身或占位符以控制对这个对象的访问。被代理的对象可以是远程的对象,创建开销大的对象或需要安全控制的对象。将一些细节复杂的操作交给代理,比如生活中的婚庆代理,房屋代理等等。
2.代理模式类图:

12模式中的模式:复合模式
1.复合模式是由模式所构成的模式,在一个解决方案中结合两个或者多个模式,以解决一般或重复发生的问题。
2.一个比较经典的就是MVC复合模式,其结合了观察者模式,策略模式和组合模式。是后端常用的模式之一。
13真实世界中的模式:与设计模式相处
1.模式的定义:模式是在某种情景下,针对某重复出现的问题的某种解决方案。
2.最后的总结:
| 装饰者 | 包装一个对象,以提供新的行为 |
| 状态 | 封装了基于状态的行为,并使用委托在行为之间切换 |
| 迭代器 | 在对象集合之间游走,而不暴露集合的实现 |
| 外观 | 简化一群类的接口 |
| 策略 | 封装可以互换的行为,并使用委托决定使用哪一个 |
| 代理 | 包装对象,以控制对此对象的访问 |
| 工厂方法 | 由子类决定要创建的具体类是哪一个 |
| 适配器 | 封装对象,并提供不同的接口 |
| 观察者 | 让对象在状态改变的时候得到通知 |
| 模板方法 | 由子类决定如何实现一个算法中的步骤 |
| 组合 | 客户用一致的方式处理对象集合和单个对象 |
| 单件 | 确保只有一个对象被创建 |
| 抽象工厂 | 允许客户创建对象的家族。而无需指定具体类 |
| 命令 | 封装请求成为对象 |
14剩下的设计模式
并非每一个模式都是受到所有人欢迎的。
桥接模式
生成器模式
责任链模式
蝇量模式
解释器模式
中介者模式
备忘录模式
原型模式
访问者模式
本文介绍了设计模式的基本概念,如观察者模式、工厂模式和单例模式等,并通过实例演示了如何在编码中应用这些设计模式,提升代码的灵活性和可维护性。深入理解策略、适配器和模板方法等模式,学习如何在实际项目中运用它们来改进软件架构。
307

被折叠的 条评论
为什么被折叠?



