Java设计模式之factory、abstract factory、proxy、observer、visitor、state、memento
提示:设计模式最好自己动手写代码实践一下,这样更有助于理解
设计模式(二)
1. Factory(工厂模式)
1.1 目的
用来解决接口的选择问题,避免向客户端暴露实例类
1.2 使用方法
为同一个接口是实例类建立一个工厂类,然后在工厂类中的工厂方法传入的参数来返回一个对应的实例类
Animal接口:
public interface Animal {
public void sound();
}
Dog类:
public class Dog implements Animal {
@Override
public void sound() {
System.out.println("汪汪");
}
}
Cat类:
public class Cat implements Animal {
@Override
public void sound() {
System.out.println("喵喵");
}
}
Factory类(工厂方法所在类):
public class Factory {
public Animal get(String str) {
if(str.toLowerCase().equals("dog")) {
return new Dog();
}else if(str.toLowerCase().equals("cat")) {
return new Cat();
}else {
return null;
}
}
}
测试代码:
public class tryness {
public static void main(String[] args) {
Factory factory = new Factory();
Animal dog = factory.get("Dog");
dog.sound();
Animal cat = factory.get("Cat");
cat.sound();
}
}
运行结果:
汪汪
喵喵
我们可以看到利用Factory来返回一个Animal的实例类,客户端不需要知道具体实例类的名称,只是传入想要的动物类型即可。
1.3 工厂方法的优缺点
优点:
调用者只需要传入名称即可,不需要知道具体的类名,扩展性较强,操作方便
缺点:
在一定程度上增加了结构的复杂性
2. Abstract Factory(抽象工厂模式)
2.1 目的
同工厂模式一样,但是如果有多个工厂模式我们想要把他们聚合起来,就需要抽象工厂方法模式了
2.2 使用方法
直接看代码,然后再解释
Anima接口:
public interface Animal {
public void sound();
}
Dog类:
public class Dog implements Animal {
@Override
public void sound() {
System.out.println("汪汪");
}
}
Cat类:
public class Cat implements Animal {
@Override
public void sound() {
System.out.println("喵喵");
}
}
Food接口:
public interface Food {
public void print();
}
Hamburger类:
public class Hamburger implements Food {
@Override
public void print() {
System.out.println("I am hamburger");
}
}
Pizza类:
public class Pizza implements Food {
@Override
public void print() {
System.out.println("I am pizza");
}
}
AbstractFactory类:
public abstract class AbstractFactory {
public abstract Animal getAnimal(String str);
public abstract Food getFood(String str);
}
AnimalFactory类:
public class AnimalFactory extends AbstractFactory {
@Override
public Animal getAnimal(String str) {
if(str.toLowerCase().equals("dog")) {
return new Dog();
}else if(str.toLowerCase().equals("cat")) {
return new Cat();
}else {
return null;
}
}
@Override
public Food getFood(String str) {
return null;
}
}
FoodFactory类:
public class FoodFactory extends AbstractFactory {
@Override
public Animal getAnimal(String str) {
return null;
}
@Override
public Food getFood(String str) {
if(str.toLowerCase().equals("hamburger")) {
return new Hamburger();
}else if (str.toLowerCase().equals("pizza")) {
return new Pizza();
}else {
return null;
}
}
}
FactoryProducer类:
public class FactoryProducer {
public static AbstractFactory getFactory(String str) {
if(str.toLowerCase().equals("animal")) {
return new AnimalFactory();
}else if(str.toLowerCase().equals("food")) {
return new FoodFactory();
}else {
return null;
}
}
}
测试代码:
public class tryness {
public static void main(String[] args) {
AbstractFactory animalFactory = FactoryProducer.getFactory("Animal");
Animal dog = animalFactory.getAnimal("Dog");
dog.sound();
Animal cat = animalFactory.getAnimal("Cat");
cat.sound();
AbstractFactory foodFactory = FactoryProducer.getFactory("Food");
Food hamburger = foodFactory.getFood("Hamburger");
hamburger.print();
Food pizza = foodFactory.getFood("Pizza");
pizza.print();
}
}
运行结果:
汪汪
喵喵
I am hamburger
I am pizza
这里的动物和事物是两个需要用工厂方法处理的类别,正常是需要两个工厂方法,但是现在想把两个工厂方法合并起来统一管理,那么就需要建立一个抽象工厂类,抽象工厂类里的两个方法自然就是返回Animal的和返回Food的,但是要确定我们需要哪个工厂方法就需要再建立一个工厂的生成类FactoryProducer,输入参数选择是animal工厂还是food工厂,然后就和工厂方法的使用方法一样了。
2.3 抽象工厂方法的优缺点
优点:
多个不相关的工厂方法被聚合到一起,方便管理
缺点:
扩展比较麻烦,而且在结构上增加了复杂程度
3. Proxy(代理模式)
3.1 目的
避免客户端对对象的直接访问,我们要在客户端和对象之间加一层代理,可以避免直接访问造成的麻烦
3.2 使用方法
先看代码,然后再解释
Pictrue接口:
public interface Picture {
public void view();
}
RealPicture类:
public class RealPicture implements Picture {
String name;
public RealPicture(String str) {
this.name = str;
}
@Override
public void view() {
System.out.println(name + " is open");
}
}
ProxyPicture类:
public class ProxyPicture implements Picture {
RealPicture rp;
String name;
public ProxyPicture(String str) {
this.name = str;
}
@Override
public void view() {
if(rp == null) {
rp = new RealPicture(name);
}
rp.view();
}
}
测试代码:
public class tryness {
public static void main(String[] args) {
ProxyPicture pp = new ProxyPicture("Doraemon");
pp.view();
}
}
运行结果:
Doraemon is open
RealPicture是真实的图像类,但是为了避免客户端直接访问此类,我们应该加一个代理ProxyPicture,用ProxyPicture来访问RealPicture,避免了直接访问的麻烦以及不安全性,在测试代码中,我们可以看到并没有出现有关RealPicture的任何信息。
3.3 代理模式的优缺点
优点:
具有可扩展性,为客户端提供了便捷
缺点:
在客户端和目标类之间加代理,一定程度上增加了复杂性
4. Observer(观察者模式)
4.1 目的
当一个对象的状态发生变化时,所有依赖于他的对象都能得到通知,并作出相应的反应
4.2 使用方法
先看代码,然后解释
Observer类:
public abstract class Observer {
Forest forest;
public abstract void upDate();
}
Dog类:
public class Dog extends Observer {
public Dog(Forest forest) {
forest.attach(this);
this.forest = forest;
}
@Override
public void upDate() {
System.out.println(this.forest.getMessage());
}
}
Cat类:
public class Cat extends Observer {
public Cat(Forest forest) {
forest.attach(this);
this.forest = forest;
}
@Override
public void upDate() {
System.out.println(this.forest.getMessage());
}
}
Forest类:
public class Forest {
private List<Observer> list = new ArrayList<Observer>();
String message = "森林安全";
public void attach(Observer o) {
list.add(o);
}
public String getMessage() {
return this.message;
}
public void changeMessage(String str) {
this.message = str;
note();
}
private void note() {
for(Observer i : list) {
i.upDate();
}
}
}
测试代码:
public class tryness {
public static void main(String[] args) {
Forest f = new Forest();
Observer dog = new Dog(f);
Observer cat = new Cat(f);
f.changeMessage("森林失火了,快跑");
f.changeMessage("森林恢复安全");
}
}
运行结果:
森林失火了,快跑
森林失火了,快跑
森林恢复安全
森林恢复安全
猫和狗都得到了相应的信息
Observer是一个抽象类,代表观察者,Forest是森林类,是所有动物的被观察者,一旦森林信息发生改变,所有动物都应该被通知到,主要就是要将所有的Observer都储存在Forest中,并且在每个Observer中都保存一个Forest的信息,用来相互关联。
4.3 观察者模式的优缺点
优点:
观察者和被观察者是耦合的,一旦被观察者发生信息变化,观察者都能及时收到通知
缺点:
如果一个对象有很多观察者时,一点的信息修改就会要全都通知一遍,很耗时,如果两个对象互为观察者,那么会无限的循环。
5. Visitor(访问者模式)
5.1 目的
需要对一个对象进行不同的不相关的操作,并且要避免这些操作影响这些对象的类,可以使用访问者模式将这些封装到类中
5.2 使用方法
先看代码,然后解释
Computer类:
public interface ComputerPart {
public void accept(Visitor visitor);
}
Mouse类:
public class Mouse implements ComputerPart {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
KeyBoard类:
public class Keyboard implements ComputerPart {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
Computer类:
public class Computer implements ComputerPart {
ComputerPart[] cp;
public Computer() {
cp = new ComputerPart[] {new Mouse(), new Keyboard()};
}
@Override
public void accept(Visitor visitor) {
for(int i = 0; i < cp.length; i ++) {
cp[i].accept(visitor);
}
}
}
Visitor类:
public interface Visitor {
public void visit(Mouse mouse);
public void visit(Keyboard keyboard);
}
MessageGotbyVisitor类:
public class MessageGotbyVisitor implements Visitor {
@Override
public void visit(Mouse mouse) {
System.out.println("This is mouse");
}
@Override
public void visit(Keyboard keyboard) {
System.out.println("This is keyboard");
}
}
测试代码:
public class tryness {
public static void main(String[] args) {
ComputerPart cp = new Computer();
MessageGotbyVisitor mgbv = new MessageGotbyVisitor();
cp.accept(mgbv);
}
}
运行结果:
This is mouse
This is keyboard
这个模式之前我虽然能看懂代码,但是并没有完全理解,这里来解释一下为什么这就是访问者模式。
首先ComputerPart是计算机组件的接口,Mouse是鼠标,KeyBoard是键盘,至于其他组件为了简便,这里就忽略了,Visitor是访问者的接口,MessageGotbyVisitor是访问后得到的信息,==visit()==方法是访问当前设备,accept() 方法代表接受访问。
我们把主体看做是设备,那么如果主体接受了访问,访问者就可以获得相应的信息,这就是访问者模式。
5.3 访问者模式的优缺点
优点:
符合单一职责的原则,扩展性强
缺点:
在一定程度上加强了结构的复杂性
6. State(状态模式)
6.1 目的
进行一定规则的状态的转换,一个易懂的例子就是实验3中的状态转换
6.2 使用方法
先看代码,再做解释
State接口:
public interface State {
public void doAction(Context context);
}
Start类:
public class Start implements State {
@Override
public void doAction(Context context) {
context.set(this);
}
public String toString() {
return "Start";
}
}
End类:
public class End implements State {
@Override
public void doAction(Context context) {
context.set(this);
}
public String toString() {
return "End";
}
}
Context类:
public class Context {
private State state;
public void set(State state) {
this.state = state;
}
public State getState() {
return this.state;
}
}
测试代码:
public class tryness {
public static void main(String[] args) {
Context c = new Context();
State start = new Start();
State end = new End();
start.doAction(c);
System.out.println(c.getState().toString());
end.doAction(c);
System.out.println(c.getState().toString());
}
}
运行结果:
Start
End
Context可以看做是我们的主要研究的对象,他的状态可变,我们可以通过操作改变他的状态,并且状态是我们限定的,State及其实例类就是设定好的状态,当然也可以加状态转换条件,类似实验3那样。
6.3 状态模式的优缺点
优点:
自定义转换规则,只能在我们设定好的状态及转换规则下进行转换
缺点:
每个状态都要有一个类,如果状态过多的话,可能会导致结构变得十分庞大,而且状态之间的转化规则也会变得十分复杂
7. Memento(备忘录模式)
7.1 目的
在不破坏封装性的条件下,获取一个对象的内部状态,并且在此对象外保存这个状态
7.2 使用方法
先看代码,再做解释
Memento类:
public class Memento {
private String message;
public Memento(String message) {
this.message = message;
}
public String getMessage() {
return this.message;
}
}
Action类:
public class Action {
private String message;
public void setMessage(String message) {
this.message = message;
}
public String getMessage() {
return this.message;
}
public Memento save() {
return new Memento(this.message);
}
}
Log类:
public class Log {
private List<Memento> memList = new ArrayList<Memento>();
public void add(Memento mem) {
memList.add(mem);
}
public Memento get(int index) {
return memList.get(index);
}
}
测试代码:
public class tryness {
public static void main(String[] args) {
Log log = new Log();
Action a = new Action();
a.setMessage("First message");
log.add(a.save());
a.setMessage("Second message");
log.add(a.save());
System.out.println(log.get(0).getMessage());
System.out.println(log.get(1).getMessage());
}
}
运行结果:
First message
Second message
其中Memento是一条备忘录,用来记录一个重要信息,Log是所有备忘录的合集,用来记录或者获取备忘录的信息,Action是可能产生重要信息的类,备忘录记录的也正是Action的信息,总的来说就是把需要被记录的信息都集中在一起进行管理。
7.3 备忘录模式的优缺点
优点:
实现了信息的储存,并且信息是可恢复的,不用担心一步的错误导致不可复原,类似Ctrl + Z 的功能
缺点:
消耗了大量的存储资源,如果需要记录的备忘信息过多,那么将会占用大量的存储空间
此篇文章只是我个人理解,如果有错误欢迎指出,感谢阅读!