(28)男人和女人——访问者模式
最后更新于:2022-04-01 14:31:03
### 访问者模式
表示一个作用于某对象结构中的各个元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。(摘抄)
从这个名字也可以看出来,这个模式就是针对于对象访问的模式,这个模式里面的每一各具体访问者,代表一个具体的操作,当你需要增加操作的时候十分方便,不过这个里面的访问者类和具体的去访问的类耦合十分紧密,所以说这个模式的局限性也很大,只适用于数据结构比较稳定的系统,它把数据结构和作用于结构上的操作之间的耦合解开了,转移到了访问者类之中。有比较稳定的数据结构,和易于变化的算法的话,使用访问者模式就十分方便。
~~~
import java.util.ArrayList;
import java.util.List;
public abstract class Visitor
{
public abstract void visitConcreteElementA(ConcreteElementA concreteElementA);
public abstract void visitConcreteElementB(ConcreteElementB concreteElementB);
}
public abstract class Element
{
public abstract void Accept(Visitor visitor);
}
public class ConcreteElementA extends Element
{
@Override
public void Accept(Visitor visitor) {
// TODO Auto-generated method stub
visitor.visitConcreteElementA(this);
}
}
public class ConcreteElementB extends Element
{
@Override
public void Accept(Visitor visitor) {
// TODO Auto-generated method stub
visitor.visitConcreteElementB(this);
}
}
public class ConcreteVisitor1 extends Visitor
{
@Override
public void visitConcreteElementA(ConcreteElementA concreteElementA) {
// TODO Auto-generated method stub
System.out.println(this.getClass()+" visit "+concreteElementA.getClass());
}
@Override
public void visitConcreteElementB(ConcreteElementB concreteElementB) {
// TODO Auto-generated method stub
System.out.println(this.getClass()+" visit "+concreteElementB.getClass());
}
}
public class ConcreteVisitor2 extends Visitor
{
@Override
public void visitConcreteElementA(ConcreteElementA concreteElementA) {
// TODO Auto-generated method stub
System.out.println(this.getClass()+" visit "+concreteElementA.getClass());
}
@Override
public void visitConcreteElementB(ConcreteElementB concreteElementB) {
// TODO Auto-generated method stub
System.out.println(this.getClass()+" visit "+concreteElementB.getClass());
}
}
public class ObjectStructure
{
private List<Element> lists = new ArrayList<Element>();
public void addElement(Element element)
{
lists.add(element);
}
public void deleteElement(Element element)
{
lists.remove(element);
}
public void Accept(Visitor visitor)
{
for (Element element : lists)
{
element.Accept(visitor);
}
}
}
class Client
{
public static void main()
{
ObjectStructure objectStructure = new ObjectStructure();
objectStructure.addElement(new ConcreteElementA());
objectStructure.addElement(new ConcreteElementB());
ConcreteVisitor1 concreteVisitor1 = new ConcreteVisitor1();
ConcreteVisitor2 concreteVisitor2 = new ConcreteVisitor2();
objectStructure.Accept(concreteVisitor1);
objectStructure.Accept(concreteVisitor2);
}
}
~~~
大多数时候你并不需要访问者模式,但当一旦你需要访问者模式时,那就是真的需要它了。(摘抄)
(26)项目多也别傻做——享元模式
最后更新于:2022-04-01 14:31:00
### 享元模式
运用共享技术有效地支持大量细粒度的对象。(摘抄)
这个模式每天在编码的时候都在用,java中的String就用到了享元模式。
~~~
String a = "123";
String b = "123";
~~~
这个只要认真学过基础的童鞋应该都知道,这两个String对象是同一个对象,都是指向“123”。在这个里面就是用的享元模式。
对于享元模式,我的理解就是新建相同的对象的时候,而且这两个对象去做相同的,那么应该引用已经创建好的对象,而不是去新建一个相同的去浪费内存。这个时候肯定有人会问要是每个对象里面有自己的标识符号,两个对象并不完全相同怎么办,这时就是后面需要提到的——内部状态和外部状态。
简单的共享对象
~~~
import java.util.HashMap;
import org.apache.commons.collections.map.HashedMap;
public abstract class Flyweight
{
public abstract void Operation(int i);
}
public class ConcreteFlyweight extends Flyweight
{
@Override
public void Operation(int i) {
// TODO Auto-generated method stub
System.out.println(""+i);
}
}
//享元工厂用来判断是否需要生成新的对象
public class FlyweightFactory
{
private HashMap<String, Flyweight> flyweights = new HashMap<String, Flyweight>();
public Flyweight getfFlyweight (String x)
{
if (!flyweights.containsValue(x))
{
flyweights.put(x, new ConcreteFlyweight());
return flyweights.get(x);
}
else
{
return flyweights.get(x);
}
}
}
class Client
{
public static void main()
{
FlyweightFactory factory = new FlyweightFactory();
Flyweight flyweight1 = factory.getfFlyweight("x");
Flyweight flyweight2 = factory.getfFlyweight("x");
}
}
//有些Flyweight不能被共享那么就写一个继承的类直接调用就可以了
~~~
最简单的共享就实现了,此时就是开始说一下内部状态和外部状态,在享元对象内部并且不会随环境改变而改变的共享部分称为内部状态随环境改变而改变,不可以共享的状态称为外部状态
当你在写程序的过程中如果能发现这些实例除了几个参数外基本相同,那么把这些参数放到享元的外部,在方法调用时将它们传递进来,就可以通过享元共享大幅度的减少单个实例的数目。(摘抄)
~~~
public class User
{
private String name;
public String getName() {
return name;
}
public User(String name) {
super();
this.name = name;
}
}
public abstract class Flyweight
{
public abstract void Operation(int i);
public abstract void whoUse(User user);
}
~~~
### 享元模式应用
如果一个应用程序使用了大量的对象,而大量的这些对象造成了很大的存储开销时就应该考虑使用;还有就是对象的大多数状态可以外部状态,如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象,此时可以考虑使用享元模式。
(25)世界需要和平——中介者模式
最后更新于:2022-04-01 14:30:58
### 中介者模式
用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显示的相互引用,从而使其耦合松散,而且可以独立改变它们之间的交互。(摘抄)
中介者模式就相当与把对象与对象之间的耦合转移到自己身上。让事物与事物之间都通过一个中介去联系,例如租房子,找中介公司,那么你就是与中介公司耦合,而不是与房东,而房东也是与中介公司耦合。
~~~
public abstract class Mediator
{
public abstract void send(String message ,Person person);
}
public abstract class Person
{
Mediator mediator;
public Person(Mediator mediator)
{
this.mediator = mediator;
}
public abstract void send(String message);
public abstract void notify(String message);
}
public class HouseMediator extends Mediator
{
private Person renter;
private Person tenant;
public void setRenter(Person renter) {
this.renter = renter;
}
public void setTenant(Person tenant) {
this.tenant = tenant;
}
@Override
public void send(String message, Person person) {
// TODO Auto-generated method stub
if (person == tenant)
{
renter.notify(message);
}
else
{
tenant.notify(message);
}
}
}
public class Renter extends Person
{
public Renter(Mediator mediator) {
super(mediator);
// TODO Auto-generated constructor stub
}
public void send(String message)
{
mediator.send(message, this);
}
public void notify(String message)
{
System.out.println("Renter"+message);
}
}
public class Tenant extends Person
{
public Tenant(Mediator mediator) {
super(mediator);
// TODO Auto-generated constructor stub
}
public void send(String message)
{
mediator.send(message, this);
}
public void notify(String message)
{
System.out.println("Tenant"+message);
}
}
class Client
{
public static void main()
{
HouseMediator houseMediator = new HouseMediator();
Person tenant = new Tenant(houseMediator);
Person renter = new Renter(houseMediator);
houseMediator.setRenter(renter);
houseMediator.setTenant(tenant);
renter.send("交房租");
tenant.send("好");
}
}
~~~
中介的出现,减少了原本有交集的两个对象在程序中的耦合,使得其可以独立发生改变和复用。
同时由于中介的控制集中化,于是把复杂的操作全部都转移到了中介中,这就造成中介十分复杂。
中介模式一般应用于一组对象以定义良好但是复杂的方式进行通信的场合,或者想定制一个分步在多个类中的行为,而又不想生成太多的子类的场合.
(24)加薪非要老总批?——职责链模式
最后更新于:2022-04-01 14:30:56
### 职责链模式
使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,知道有一个对象处理它为止。(摘抄)
其实这个职责链模式有点像数据结构里面的并查集,找处理对象的过程,就像是找父亲的过程。
使用这种模式处理一些请求很方便,因为每一个类都可以只处理一种情况,当自己处理不了之后,就交给下一个类去处理,这样让每一个处理类都分工明确,而且转换十分灵活,可以随时增减处理对象和修改处理请求。
~~~
public abstract class Request
{
Request request;
public void setSuccessRequest(Request request)
{
this.request = request;
}
public abstract void doRequest(int req);
}
public class ConcreteRequest1 extends Request
{
@Override
public void doRequest(int req) {
// TODO Auto-generated method stub
if (req == 1)
{
System.out.println("req == 1");
}
else
{
request.doRequest(req);
}
}
}
public class ConcreteRequest2 extends Request
{
@Override
public void doRequest(int req) {
// TODO Auto-generated method stub
if (req != 1)
{
System.out.println("req != 1");
}
}
}
class Client
{
public static void main()
{
Request request1 = new ConcreteRequest1();
Request request2 = new ConcreteRequest2();
request1.setSuccessRequest(request2);
request1.doRequest(1);
}
}
~~~
通过代码可以很明显的看出只要构建好这条链那么处理只需要用这条链的头节点请求就可以了。
(23)烤羊肉串引来的思考——命令模式
最后更新于:2022-04-01 14:30:54
### 命令模式
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。(摘抄)
命令模式我感觉结合代码来讲会比较好讲,所以我先写代码把
~~~
import java.util.ArrayList;
import java.util.List;
//Action类,执行具体的操作
public class Action
{
void actionA()
{
System.out.println("A");
}
void actionB()
{
System.out.println("B");
}
}
//Command类,用来声明执行操作的接口
public abstract class Command
{
Action action;
public Command(Action action)
{
this.action = action;
}
public abstract void execute();
}
public class ConcreteCommandA extends Command
{
public ConcreteCommandA(Action action)
{
super(action);
}
@Override
public void execute() {
// TODO Auto-generated method stub
action.actionA();
}
}
public class ConcreteCommandB extends Command
{
public ConcreteCommandB(Action action)
{
super(action);
}
@Override
public void execute() {
// TODO Auto-generated method stub
action.actionB();
}
}
//Invoker类,要求该命令执行这个操作
public class Invoker
{
private List<Command> commands = new ArrayList<Command>();
//在add和delete方法中可以执行排队请求和记录日志和否决请求操作
public void addCommand(Command command)
{
commands.add(command);
}
public void deleteCommand(Command command)
{
commands.remove(command);
}
public void executeCommand()
{
for (Command command : commands)
{
command.execute();
}
}
}
class Client
{
public static void main()
{
Action action = new Action();
Command aCommand = new ConcreteCommandA(action);
Command bCommand = new ConcreteCommandB(action);
Invoker invoker = new Invoker();
invoker.addCommand(aCommand);
invoker.addCommand(bCommand);
invoker.deleteCommand(aCommand);
invoker.executeCommand();
}
}
~~~
一个命令模式的模板差不多就是这样了,从这个模板可以很清晰的看出来要添加新的命令也十分容易,删除也很容易,添加新的命令类,更是这里的一大亮点,当你需要一个ConcreteCommandC来同时执行actionA和actinoB那么新建一个ConcreteCommandC类就可以了,在execute方法中,添加这两个方法的执行就ok,对于设计模式的一些原则运用得十分到位。
命令模式把请求一个操作的对象与知道怎么执行一个操作的对象分隔开了。(摘抄)
### 命令模式的作用
1.它能较容易地设计一个命令队列。
2.在需要的情况下,可以较容易地将命令记入日志。
3.允许接受请求的一方决定是否要否决请求。
4.可以容易的实现对请求的撤销和重做。
5.由于加进新的具体命令类不影响其他的类,因此添加新的具体命令类很容易。
(摘抄)
### 注意
敏捷开发原则告诉我们,不要为代码添加基于猜测的,实际不需要的功能。如果不清楚一个系统是否需要命令模式,一般就不要急着去实现它,事实上,在需要的时候通过重构实现这个模式并不困难,只有在真正需要如撤销/恢复操作等功能时,把原来的代码重构为命令模式才有意义。(摘抄)
(22)手机软件何时统一——桥接模式
最后更新于:2022-04-01 14:30:51
### 合成/聚合复用原则
尽量使用合成/聚合,尽量不要使用类继承。(摘抄)
其实合成/聚合复用原则,平时都会使用,但是自己总结可能有点总结不出来。这个原则用我的理解就是抽象与抽象之间划分必须明确,尽量不要让一个抽象去继承与另一个抽象,要让一个抽象去调用另一个抽象。
继承的使用必须要十分谨慎,因为使用继承之后,父类发生改变,必然会影响到子类。继承使用时必须要确定当前类和父类是有非常紧密的关系,不然会对当前类的复用产生很大的影响。要从当前类的职责来考虑问题。
聚合表示一种弱的“拥有”关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分;合成则是一种强的“拥有”关系,体现了严格的部分和整体的关系,部分和整体的生命周一样。(摘抄)
合成/聚合复用原则的好处是,优先使用对象的合成/聚合将有助于你保持每个类被封装,并集中在单个任务上。这样类和类继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大物。(摘抄)
### 桥接模式
将抽象部分与它的实现部分分离,使它们都可以独立地变化。(摘抄)
这句话地意思应该是把它的每个功能和功能之间都分开。然后这些功能和整个物体的抽象也分开。
这个功能的说法并不是一定代表一个具体的功能,它是这个系统的某一个实现,暂且把实现就当成实现一个功能
~~~
//功能接口
public interface Implementor
{
void Operation();
}
//功能的实现A&B
public class ConcreteImplA implements Implementor
{
@Override
public void Operation() {
// TODO Auto-generated method stub
System.out.println("A");
}
}
public class ConcreteImplB implements Implementor
{
@Override
public void Operation() {
// TODO Auto-generated method stub
System.out.println("B");
}
}
//整个物体的抽象
public class Abstract
{
public Implementor implementor;
public void setImplementor(Implementor implementor) {
this.implementor = implementor;
}
public void Operation()
{
implementor.Operation();
}
}
//某个子物体
public class RefinedAbstract extends Abstract
{
@Override
public void Operation()
{
//可以添加特有的一些操作
implementor.Operation();
}
}
class Client
{
public static void main()
{
Abstract ab = new RefinedAbstract();
ab.setImplementor(new ConcreteImplA());
ab.Operation();
ab.setImplementor(new ConcreteImplB());
ab.Operation();
}
}
~~~
实现系统啃呢个有多种角度分类,每一种分类都有可能变化,那么就把这种多角度分离出来让它们独立变化,减少它们之间的耦合。(摘抄)—— 感觉这句话总结得很精辟。
(21)有些类也需计划生育——单例模式
最后更新于:2022-04-01 14:30:49
### 单例模式
保证一个类仅有一个实例,并提供一个访问它的全局访问点。(摘抄)
解决的问题:保证一个类在内存中的对象唯一性。
比如:多程序读取一个配置文件时,建议配置文件封装成对象。会方便操作其中数据,又要保证多个程序读到的是同一个配置文件对象,就需要该配置文件对象在内存中是唯一的。
~~~
public class Singleton
{
private Singleton(){}
private static Singleton singleton;
public static Singleton getSingleton()
{
if (singleton == null)
{
singleton = new Singleton();
}
return singleton;
}
}
class Client
{
public static void main()
{
Singleton singleton1 = Singleton.getSingleton();
Singleton singleton2 = Singleton.getSingleton();
if (singleton1 == singleton2)
{
System.out.println("yes");
}
else
{
System.out.println("no");
}
}
}
~~~
上面这种单例模式的类加载方式被称为懒汉式(延迟加载方式)
下面这种为饿汉式
~~~
public class Singleton
{
private Singleton(){}
private static Singleton singleton = new Singleton();
public static Singleton getSingleton()
{
return singleton;
}
}
~~~
饿汉式在多线程时不会重复生成实例,因为静态初始化让它在程序加载的时候已经生成实例了,懒汉式如果要在多线程时也不出问题,那么需要双重锁定
~~~
public class Singleton
{
private Singleton(){}
private static Singleton singleton;
public static Singleton getSingleton()
{
if (singleton == null)
{
synchronized (Singleton.class)
{
if (singleton == null)
{
singleton = new Singleton();
}
}
}
return singleton;
}
}
~~~
这样懒汉式也可以在多线程时调用了。
(20)想走?可以!先买票——迭代器模式
最后更新于:2022-04-01 14:30:47
### 迭代器模式
提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。(摘抄)
当你需要遍历一个聚集对象的时候应该考虑迭代器模式,其原理也就是把一个聚集对象从头到尾走一遍,现在大多数语言都进行了封装,所以其实不需要过多讲解
~~~
for (iterable_type iterable_element : iterable)
{
}
~~~
(19)分公司=部门——组合设计模式
最后更新于:2022-04-01 14:30:45
### 组合模式
将对象组合成树形结构以表示”部分-整体“的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
举个例子,一个车是由发动机,轮子,车架,轮胎组成,那么从层次上来划分肯定是
车 第一层
车架,发动机,轮子 第二层
轮胎 第三层
那么从程序上可以把这些都当成元素来处理,区别在于大元素和小元素,大元素可以添加小元素,例如车可以添加轮子,车架和发动机。轮子可以添加轮胎。车架,发动机和轮胎则不能添加东西。把这些元素组合起来就可以组成一辆车,而这辆车也可以成为f1赛车的一个元素,同时轮胎也可以成为f1赛车的一个元素。
首先我们定义一个Component类,然后分大小元素都去继承与这个Component类,再分开去实现分支节点(大元素)和叶子节点(小元素)。
~~~
import java.util.ArrayList;
import java.util.List;
public abstract class Component
{
public String name;
public Component(String name)
{
this.name = name;
}
public abstract void add(Component c);
public abstract void delete(Component c);
}
public class Composite extends Component
{
public Composite(String name) {
super(name);
// TODO Auto-generated constructor stub
}
private List<Object> list = new ArrayList<Object>();
@Override
public void add(Component c) {
// TODO Auto-generated method stub
list.add(c);
}
@Override
public void delete(Component c) {
// TODO Auto-generated method stub
list.remove(c);
}
}
public class Leaf extends Component
{
public Leaf(String name) {
super(name);
// TODO Auto-generated constructor stub
}
@Override
public void add(Component c) {
// TODO Auto-generated method stub
//叶子节点不需要添加操作
}
@Override
public void delete(Component c) {
// TODO Auto-generated method stub
//叶子节点不需要删除操作
}
}
class Client
{
public static void main()
{
Composite root = new Composite("root");
car.add(new Leaf("Leaf A"));
car.add(new Leaf("Leaf B"));
Composite branch = new Composite("branch");
wheel.add(new Leaf("Leaf C"));
car.add(branch);
}
}
~~~
这个时候有人会问,既然叶子节点不需要add和remove那么为什么要写到Component类里面?其实这里就有两种方式:透明方式与安全方式。
透明方式:在Component中申明所有用来管理子对象的方法,好处是所有的子类的方法接口都一样,缺点是有些元素不需要实现一些方法。
安全方式:在Component中不去申明所有的管理子对象的方法,例如add和remove,把add和remove写到Composite里面去申明。这样由于分支节点和叶子节点将不具有相同的接口,客户端调用需要做相应的判断。
### 何时使用组合模式
需求中是体现部分与整体的层次结构时,以及你希望用户可以忽略组合对象与单个对象的不同,统一的使用组合结构中所有的对象时,就应该考虑组合模式了。(摘抄)
(18)如果再回到从前——备忘录模式
最后更新于:2022-04-01 14:30:42
### 备忘录模式
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将这个对象恢复到原先保存的状态。(摘抄)
备忘录模式可以就用平时备忘东西的过程来理解,首先你要备忘,必须要知道备忘什么东西,这时就可以把备忘的东西抽出来当一个类Memento,然后你要有一个产生备忘的类,是谁要备忘Originator,最后备忘的东西放在哪,需要的时候去哪里取出来Caretaker。
~~~
//备忘录类
public class Memento
{
private String state;
public Memento(String state)
{
this.state = state;
}
public String getState()
{
return state;
}
}
//生成备忘录的类
public class Originator
{
private String state;
public void setState(String state)
{
this.state = state;
}
public void printState()
{
System.out.println(state);
}
public Memento createMemento()
{
return new Memento(state);
}
public void setMemento(Memento memento)
{
state = memento.getState();
}
}
//存储备忘录的类
public class Save
{
private Memento memento;
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
}
class client
{
public static void main()
{
Originator originator = new Originator();
originator.setState("start");
originator.printState();
//保存状态
Save save = new Save();
save.setMemento(originator.createMemento());
originator.setState("stop");
originator.printState();
//恢复状态
originator.setMemento(save.getMemento());
originator.printState();
}
}
~~~
要保存的细节全部都封装在Memento类里面了,如果需要改变保存的细节,那么也和客户端没有关系。这就是备忘录模式最大的好处,例如你一开始想保存A属性,但是后来又想保存B属性,这个时候只需要改动Originator和Memento,客户端完全是可以继续用的。
Memento模式比较适合用于功能比较复杂的,但需要维护或记录属性历史的类,或者需要保存属性只是众多属性中的一小部分时,Originator可以根据保存的Memento信息还原到前一个状态。(摘抄)
(17)在NBA我需要翻译——适配器模式
最后更新于:2022-04-01 14:30:40
### 适配器模式
将一个类的接口转换成用户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。(摘抄)
其实适配器模式很简单,就是当你发现你写好了抽象接口和实现类,但是实现的类方法和抽象接口的方法不兼容,这个时候加入一个类实现抽象接口,然后在这个类中实例化实现类,在接口需要调用某种方法时,直接调用实现类里面的方法就可以了,这样解除了不兼容的问题。但是缺点也很明显,如果实现类中的代码不是特别复杂而且这个实现类又和别的没有太大关系时,最好是重新写一个接口的实现类会比较方便和清晰。同时,这也体现了适配器模式的优点,可以增加类的复用,同时在后期维护过程中只需要增加适配器类就可以了。这个是利弊共存的,至于如何取舍这就需要具体情况具体分析了。
~~~
//定义抽象接口
public interface DBTool
{
void query();
void update();
void delete();
void add();
}
//程序里已经有了一个写好的数据库处理类
public class ConcreteDBTool
{
public void q()
{}
public void u()
{}
public void d()
{}
public void a()
{}
}
//已经存在的处理类和接口不兼容,这时就需要一个适配器类
public class DBToolAdapter implements DBTool
{
private ConcreteDBTool concreteDBTool = new ConcreteDBTool();
@Override
public void query() {
// TODO Auto-generated method stub
concreteDBTool.q();
}
@Override
public void update() {
// TODO Auto-generated method stub
concreteDBTool.u();
}
@Override
public void delete() {
// TODO Auto-generated method stub
concreteDBTool.d();
}
@Override
public void add() {
// TODO Auto-generated method stub
concreteDBTool.a();
}
}
//如果客户端现在需要使用已经写好的数据库实现类,那么使用适配器类就可以了
public class Client
{
public static void main()
{
DBTool dbTool = new DBToolAdapter();
//使用的是已经提前写好的实现类里面的方法
dbTool.query();
dbTool.update();
dbTool.delete();
dbTool.add();
}
}
~~~
平时编程的时候有很多类的名字里面都有adapter,估计这些adapter类,就是使用的适配器模式![微笑](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-18_569ca449c5105.gif)
。(纯属个人猜测)
(16)无尽加班何时休息——状态模式
最后更新于:2022-04-01 14:30:38
### 状态模式
当一个对象的内在状态改变时允许其改变其行为,这个对象看起来像是改变了其类。(摘抄)
状态模式主要用于解决状态转换过程中代码过于复杂。把状态的判断逻辑放到用于表示状态的一系列类中去实现。这样就可以把复杂的逻辑判断给省略。
~~~
public class Worker
{
public State state;
public Worker(State state)
{
this.state = state;
}
public void Request()
{
state.handler(this);
}
}
public abstract class State
{
public abstract void handler(Worker worker);
}
public class ConcreteStateA extends State
{
@Override
public void handler(Worker worker) {
// TODO Auto-generated method stub
worker.state = new ConcreteStateB();
}
}
public class ConcreteStateB extends State
{
@Override
public void handler(Worker worker) {
// TODO Auto-generated method stub
worker.state = new ConcreteStateA();
}
}
class Client
{
public static void main()
{
Worker worker = new Worker(new ConcreteStateA());
worker.Request();
}
}
~~~
基本的实现代码就是上面这样了,这个模式相当于把所有与状态转换相关的代码都放到了State类子类的handler函数里面。当你需要增加新的状态的时候十分方便添加。
这个模式首先把每一状态都分开了,互不干扰,其次它们在状态转换的时候也没有用到大量的分支语句,减少了增减功能时意外的发生。
当一个对象的行为取决与它的状态,并且它必须在运行时刻根据状态的改变改变它的行为时,就可以考虑使用状态模式。(摘抄)
(15)就不能不还DB吗?—— 抽象工厂模式
最后更新于:2022-04-01 14:30:36
### 抽象工厂模式
提供一个创建一系列相关或互相依赖对象的接口,而无需指定它们具体的类。(摘抄)
抽象工厂模式是工厂模式的一种拓展吧——这个工厂需要创建多个类,而这些类共同组成一个系列
~~~
//首先确定好产品的抽象类
public abstract class AbstractProductA
{
}
public abstract class AbstractProductB
{
}
//A和B组成一个系列产品
public class ProductA1 extends AbstractProductA
{
}
public class ProductB1 extends AbstractProductB
{
}
public class ProductA2 extends AbstractProductA
{
}
public class ProductB2 extends AbstractProductB
{
}
//产品类定义完了之后开始定义工厂类
public abstract class AbstractFactory
{
public abstract AbstractProductA createProductA();
public abstract AbstractProductB createProductB();
}
public class ConcreteFactory1 extends AbstractFactory
{
@Override
public AbstractProductA createProductA() {
// TODO Auto-generated method stub
return new ProductA1();
}
@Override
public AbstractProductB createProductB() {
// TODO Auto-generated method stub
return new ProductB1();
}
}
public class ConcreteFactory2 extends AbstractFactory
{
@Override
public AbstractProductA createProductA() {
// TODO Auto-generated method stub
return new ProductA2();
}
@Override
public AbstractProductB createProductB() {
// TODO Auto-generated method stub
return new ProductB2();
}
}
class Client
{
public static void main()
{
// AbstractFactory factory = new ConcreteFactory2();
AbstractFactory factory = new ConcreteFactory1();
AbstractProductA productA = factory.createProductA();
AbstractProductB productB = factory.createProductB();
}
}
~~~
抽象工厂模式好处有两点:
1.工厂只需要初始化一次,当产品需要替换的时候只需要替换很少的代码
2.客户端在使用的时候都是通过抽象接口在操纵实例,具体的类没有暴露在外面
缺点:
同样也是工厂模式的缺点,就是增加一个产品的时候需要增加很多类和代码。
### 解决办法:
1.用简单工厂模式,当增加功能的时候只需要增加switch case语句,但是违背了开放-封闭原则,而且由于需要生产的类很多,所以switch case也会很多,所以不可取。
2.用反射机制,动态的去生产所需要的类
~~~
public class AbstractFactory
{
private String s;
public AbstractFactory(String s)
{
this.s = s;
}
public AbstractProductA createProductA()
{
return (AbstractProductA) Class.forName("ProductA"+s).newInstance();
}
public AbstractProductB createProductB()
{
return (AbstractProductB) Class.forName("ProductB"+s).newInstance();
}
}
class Client
{
public static void main()
{
AbstractFactory factory = new AbstractFactory("1");
AbstractProductA productA = factory.createProductA();
AbstractProductB productB = factory.createProductB();
}
}
~~~
当需要添加一个功能的时候在工厂类中增加一个生产方法就好了
在简单工厂模式中用到switch case的地方也可以考虑用反射机制来解除耦合。
java实现事件委托
最后更新于:2022-04-01 14:30:33
### 事件委托
委托就像是拿另一种方法替代了原本的方法,交给现在这个替代后的方法使用,使用时和原来的方法没有区别。
在c#里面语法中就有委托这个概念,所以实现起来十分的方便,可是在java中没有,就只能自己用发射的一些机制来实现了。
在java中实现委托,首先需要定义一个事件类,里面包含了调用的对象,调用的方法名,方法所需参数,和参数的类型
~~~
package com.suski.delegate;
import java.lang.reflect.Method;
public class Event {
private Object object;
private String methodName;
private Object[] params;
private Class[] paramTypes;
public Event(Object object,String method,Object...args)
{
this.object = object;
this.methodName = method;
this.params = args;
contractParamTypes(this.params);
}
private void contractParamTypes(Object[] params)
{
this.paramTypes = new Class[params.length];
for (int i=0;i<params.length;i++)
{
this.paramTypes[i] = params[i].getClass();
}
}
public void invoke() throws Exception
{
Method method = object.getClass().getMethod(this.methodName, this.paramTypes);//判断是否存在这个函数
if (null == method)
{
return;
}
method.invoke(this.object, this.params);//利用反射机制调用函数
}
}
~~~
事件类定义完成了,就可以使用委托了,把调用方法的对象,方法名,和参数传进来就好了。
### 弥补观察者模式缺点
如果想做到昨天观察者模式实现的效果(一个类改变通知好几个类),那么就还需要定义一个事件管理队列的类
~~~
package com.suski.delegate;
import java.util.ArrayList;
import java.util.List;
public class EventHandler {
private List<Event> objects;
public EventHandler()
{
objects = new ArrayList<Event>();
}
public void addEvent(Object object, String methodName, Object...args)
{
objects.add(new Event(object, methodName, args));
}
public void notifyX() throws Exception
{
for (Event event : objects)
{
event.invoke();
}
}
}
~~~
这个类就是把事件都放入一个List中,到时候在一次取出来
队列定义完了,那么就可以定义通知的抽象类了
~~~
package com.suski.delegate;
public abstract class Notifier {
private EventHandler eventHandler = new EventHandler();
public EventHandler getEventHandler()
{
return eventHandler;
}
public void setEventHandler(EventHandler eventHandler)
{
this.eventHandler = eventHandler;
}
public abstract void addListener(Object object,String methodName, Object...args);
public abstract void notifyX();
}
~~~
再定义具体的实现类
~~~
package com.suski.delegate;
public class ConcreteNotifier extends Notifier{
@Override
public void addListener(Object object, String methodName, Object... args) {
this.getEventHandler().addEvent(object, methodName, args);
}
@Override
public void notifyX() {
try {
this.getEventHandler().notifyX();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
~~~
这时再定义需要通知的对象类就可以了
~~~
package com.suski.delegate;
import java.util.Date;
public class WatchingTVListener {
public WatchingTVListener()
{
System.out.println("watching TV");
}
public void stopWatchingTV(Date date)
{
System.out.println("stop watching" + date);
}
}
package com.suski.delegate;
import java.util.Date;
public class PlayingGameListener {
public PlayingGameListener()
{
System.out.println("playing");
}
public void stopPlayingGame(Date date)
{
System.out.println("stop playing" + date);
}
}
~~~
测试的方法
~~~
package com.suski.delegate;
import java.util.Date;
public class Test {
public static void main (String[] args)
{
Notifier goodNotifier = new ConcreteNotifier();
PlayingGameListener playingGameListener = new PlayingGameListener();
WatchingTVListener watchingTVListener = new WatchingTVListener();
goodNotifier.addListener(playingGameListener, "stopPlayingGame", new Date());
goodNotifier.addListener(watchingTVListener, "stopWatchingTV", new Date());
goodNotifier.notifyX();
}
}
~~~
这样就相当于c#的委托了,这样也改掉了观察者模式的缺点,通知者类完全不知道自己需要通知的是谁,做到了完全解耦,同时也去掉了抽象的观察者类。
昨天完全不明白java委托要怎么做,后来终于找到了一个,感觉这个还不错,就写出来和大家分享一下。
(14)老板回来,我不知道——观察者模式
最后更新于:2022-04-01 14:30:31
### 观察者模式
定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。(摘抄)
观察者模式可以大大的降低类与类之间的耦合,同时又能保持一些对象改变的一致性。
观察者模式把相互调用的类,抽象成了两个部分,它们之间互不干扰,可以各自独立的改变和使用,其中一个部分依赖于另一个部分的抽象,当一个部分改变时,可以通知另一个部分及时发生改变。
~~~
import java.util.ArrayList;
abstract class Subject
{
private ArrayList<Observer> list = new ArrayList<Observer>();
private String subjectState;
public void Attach(Observer observer)
{
}
public void Detach(Observer observer)
{
}
public void Notify()
{
for (Observer o : list)
{
o.update();
}
}
}
interface Observer
{
void update();
}
class ConcreteSubject1 extends Subject
{
public void Notify()
{
System.out.println("abcd");
}
}
class ConcreteSubject2 extends Subject
{
public void Notify()
{
System.out.println("bcde");
}
}
class ConcreteObserver1 implements Observer
{
private Subject subject;
public ConcreteObserver1(Subject subject)
{
this.subject = subject;
}
@Override
public void update() {
// TODO Auto-generated method stub
System.out.println("1"+subject);
}
}
class ConcreteObserver2 implements Observer
{
private Subject subject;
public ConcreteObserver2(Subject subject)
{
this.subject = subject;
}
@Override
public void update() {
// TODO Auto-generated method stub
System.out.println("2"+subject);
}
}
class Client
{
public static void main()
{
Subject subject = new ConcreteSubject1();
subject.Attach(new ConcreteObserver1(subject));
subject.Attach(new ConcreteObserver2(subject));
subject.Notify();
//更换主题类
// Subject subject = new ConcreteSubject2();
//
// subject.Attach(new ConcreteObserver1(subject));
// subject.Attach(new ConcreteObserver2(subject));
//
// subject.Notify();
}
}
~~~
话说我感觉这一章这本书好像没怎么讲明白,所以感觉自己说呢也没有说明白,不过差不多理解什么意思了,我感觉树上前面的代码完全就还是耦合的,还是在ConcreteObserver类中调用了ConcreteSubject类,它没有用到抽象的类,我感觉可能书上弄错了。
观察者模式优点:就是类与类之间依赖的是抽象对象,而不是具体对象,当具体对象更改时对于整个代码是没有影响的,同时是多个对象依赖于一个抽象对象,当对象状态发生变化时通知其它对象发生相应的改变比较方便
观察者模式缺点:抽象通知者依赖的是抽象观察者,当抽象观察者产生的时间在抽象通知者之后,那么就没有update方法给抽象观察者调用了。
### 委托
用委托的方法可以解除掉观察者模式的缺点,在c#中有委托这种用法,在java中就要使用到动态代理,需要理一理思路,查一查文章,等下再写一篇专门说java动态代理的吧
(13)好菜每回味不同——建造者模式
最后更新于:2022-04-01 14:30:29
### 建造者模式
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。(摘抄)
通俗来说就是你只需要指定创建的类型是什么,创建的过程不变,就可以得到不同的表示对象。
使用建造者模式首先定义一个产品类,然后通过产品类知道需要具体建造哪些部分,然后创建建造者类,定义好创建的抽象方法,再创建具体建造者类,实现具体的内容,最后创建一个指挥者类,来指挥建造过程.
~~~
import javax.naming.directory.DirContext;
class Product
{
a,b,c;
public void show()
{
System.out.println(a+b+c);
}
}
abstract class Builder
{
public abstract void BuilderA();
public abstract void BuilderB();
public abstract void BuilderC();
public abstract Product getProduct();
}
class ConcreteBuilder1 extends Builder
{
private Product product = new Product();
@Override
public void BuilderA()
{
product.a = 功能3;
}
@Override
public void BuilderB() {
// TODO Auto-generated method stub
product.b = 功能4;
}
@Override
public void BuilderC() {
// TODO Auto-generated method stub
product.c = 功能5;
}
@Override
public Product getProduct() {
// TODO Auto-generated method stub
return product;
}
}
class ConcreteBuilder2 extends Builder
{
private Product product = new Product();
@Override
public void BuilderA() {
// TODO Auto-generated method stub
product.a = 功能6;
}
@Override
public void BuilderB() {
// TODO Auto-generated method stub
product.b = 功能7;
}
@Override
public void BuilderC() {
// TODO Auto-generated method stub
product.c = 功能8;
}
@Override
public Product getProduct() {
// TODO Auto-generated method stub
return product;
}
}
class Director
{
public void Construct(Builder builder)
{
builder.BuilderA();
builder.BuilderB();
builder.BuilderC();
}
}
class Client
{
public static void main()
{
Director director = new Director();
ConcreteBuilder1 b1 = new ConcreteBuilder1();
ConcreteBuilder2 b2 = new ConcreteBuilder2();
director.Construct(b1);
Product p1 = b1.getProduct();
director.Construct(b1);
Product p2 = b2.getProduct();
}
}
~~~
建造者模式把创建对象时装配的代码隐藏起来了,当创建新的对象的时候不需要了解创建的过程,只需要知道需要创建哪一种对象即可。
建造者模式创建的对象一般都是内部结构的建造顺序通常时稳定的,但对象内部的构建通常面临者许多变化。
同时用建造者模式创建复杂的对象,在给其添加功能时可以节约很多代码,相当于把很多重复的代码写到了具体的创建类中,同时需要增加具有新功能的对象的时候只需要再多写一个具体建造者类就可以了。
### 总结
建造者模式是在当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时适用的模式。(摘抄)
当使用建造建议灵活使用,不要呆板的去套用这个规则,例如这个对象在整个程序中都只需要创建一次,那就没有必要再去把它抽象成这种建造者模式了。
(12)牛市股票还会亏钱?—— 外观模式
最后更新于:2022-04-01 14:30:26
### 外观模式
为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。(摘抄)
外观模式体现了依赖倒转原则和迪米特法则,抽出来一个外观类作为客户端调用接口,当客户端调用的时候只需要知道外观类的方法和实现的效果即可,而不需要去知道具体的功能类做了那些工作,其实仔细看外观模式也会有很多前面提到过的设计模式的影子,这些影子就是设计模式的原则和法则,所以把原则弄明白了一切的设计模式都不在话下,会被我们踩在脚下的![奋斗](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c446a99dec4.gif)
~~~
class A
{
public void methodA()
{
//A的操作
}
}
class B
{
public void methodB()
{
//B的操作
}
}
class C
{
public void methodC()
{
//C的操作
}
}
class D
{
public void methodD()
{
//D的操作
}
}
class Facade
{
A a;
B b;
C c;
D d;
public Facede() {
// TODO Auto-generated constructor stub
a = new A();
b = new B();
c = new C();
d = new D();
}
public void method1()
{
a.methodA();
b.methodB();
}
public void method2()
{
c.methodC();
d.methodD();
}
}
class Client
{
public static void main()
{
Facede facade = new Facade();
facade.method1();
facade.method2();
}
}
~~~
简单的代码实现就在上面了,首先要弄清楚这个外观模式在什么时候调用,外观模式是一个提供给客户调用功能类的接口,他自己本身是和功能类没有任何关系的。
在平时给软件设计系统时也应该做到把层与层之间的划分做得很清晰,同时随着功能类的越来越多,提供一个简单的调用接口,可以有效的减少层与层之间的耦合。
他的好处还有当你要给客户端修改调用的功能类时直接更改外观类中的代码就行了。
同时当你需要给一个别人写的软件拓展功能的时候,例如一个小插件,但是如果这个软件很庞大,代码调用很繁杂,这时你开发一个Facade类,把需要调用的类和一些代码处理的工作交给Facade,那你开发小插件调用的时候就会很方便而且很清晰,因为Facade都已经整理好了,而且你要是要写多个小插件,但是功能又有重叠的地方,那么这个Facade的作用就更大了![惊讶](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-20_569f5ccae167f.gif)
(11)迪米特法则
最后更新于:2022-04-01 14:30:24
### 迪米特法则
如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。
迪米特法则首先强调的前提是在类的结构设计上,每一个类都应当尽量降低成员的访问权限。(摘抄)[多用共有方法去访问似有变量,做好类的封装]
其实迪米特法则的核心就是减少类与类之间的耦合,类之间的耦合越弱,当其中一个类受到影响的时候其余不会受到波及。
这个法则在前面的一些模式中都有体现,当需要调用这个类的时候都是使用的生成父类对象,然后再向上转型。
(10)考题抄错会做也白搭——模板方法模式
最后更新于:2022-04-01 14:30:22
### 模板方法模式
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。(摘抄)
模板方法模式使一个比较简单的模式,平时其实编程中肯定用得挺多的,只是不知道叫这个名字罢了,模板方法模式就是把一些固定的行为放到超类中去,去掉这些重复的代码从而减少代码量,这样就提供了一个复用性很好的代码平台。
回调中其实就用到了模板方法模式。
举个生活中很简单的例子,你要到开门进入房间,那么过程总是先开门,再进入房间。开门的方法有很多种,密码,声控,钥匙等,进入房间的方法也有很多种,走进,跑进,爬进,跳进等。那么这时就可以把开门进入房间这个固定的过程作为一个模板,然后具体的方法具体继承实现。
~~~
abstract class OpenCome
{
public abstract void open();
public abstract void come();
public void templateMethod()
{
open();
come();
System.out.println("我已进入");
}
}
class KeyOpenWalkCome extends OpenCome
{
@Override
public void open() {
// TODO Auto-generated method stub
System.out.println("钥匙开门");
}
@Override
public void come() {
// TODO Auto-generated method stub
System.out.println("走入门内");
}
}
class VoiceOpenWalkCome extends OpenCome
{
@Override
public void open() {
// TODO Auto-generated method stub
System.out.println("声控开门");
}
@Override
public void come() {
// TODO Auto-generated method stub
System.out.println("跑入门内");
}
}
class Client
{
public static void main()
{
OpenCome openCome = new KeyOpenWalkCome();
openCome.templateMethod();
openCome = new VoiceOpenWalkCome();
openCome.templateMethod();
}
}
~~~
上面举的开门进入的例子代码差不多就是这样实现,当你需要改变实现方法时继承父类重写动作就好了。把一些重复的代码抽象出来变成模板,这样自己使用会更方便,同时代码结构也会比较清晰
(9)简历复印——原型模式
最后更新于:2022-04-01 14:30:20
### 原型模式
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象(摘抄)
原型模式的作用在于多次创建类似对象时可以减少构造函数使用,减少代码,同时也可以提高性能。通过查看别人的博客和一些资料得知,用clone方法复制对象是直接对内存操作比重新生成一个对象效率高很多,尤其是数据十分多的对象。
~~~
class Prototype implements Cloneable
{
public Object clone()
{
return (Object)this.clone();
}
}
class Client{
public static void main()
{
Prototype a = new Prototype();
while(1){
Prototype B = (Prototype) a.clone();
}
}
}
~~~
原型模式是很简单的模式,继承一个接口,重写一个方法就ok了,需要注意的一点就是 深拷贝 和 浅拷贝
java的基本数据类型会clone方法会直接深拷贝,而引用数据类型则需要自己手动深拷贝
~~~
class Prototype implements Cloneable
{
String a;
public Object clone()
{
Prototype prototype = (Prototype) this.clone();
prototype.a = this.a;
return prototype;
}
}
~~~
### 总结
因为我对原型模式的应用比较少,以前都没有怎么考虑到效率的问题,这次学到后,以后会多留意留意。
个人感觉原型模式最大的好处就是新建对象时不需要再一个参数一个参数的去设置了,因为有些参数是相同的,直接clone方便很多,尤其是今天看到大家都说原型模式运用得好会提升整个软件的运行效率后,感觉原型模式好处也是大大滴!!!