创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
下面主要来自网上我看到的解释的比较简洁易懂的例子的组合。常用的设计模式,一些不常用的我就没说。
单例模式
//饿汉式单例类.在类初始化时,已经自行实例化
public class Singleton1 {
//私有的默认构造子
private Singleton1() {}
//已经自行实例化
private static final Singleton1 single = new Singleton1();
//静态工厂方法
public static Singleton1 getInstance() {
return single;
}
}
//懒汉式单例类.在第一次调用的时候实例化
public class Singleton2 {
//私有的默认构造子
private Singleton2() {}
//注意,这里没有final
private static Singleton2 single=null;
//静态工厂方法
public synchronized static Singleton2 getInstance() {
if (single == null) {
single = new Singleton2();
}
return single;
}
String name = null;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void printInfo() {
System.out.println("the name is " + name);
}
public class TMain {
public static void main(String[] args){
TestStream ts1 = Singleton2.getInstance();
ts1.setName("jason");
TestStream ts2 = Singleton2.getInstance();
ts2.setName("0539");
ts1.printInfo();
ts2.printInfo();
if(ts1 == ts2){
System.out.println("创建的是同一个实例");
}else{
System.out.println("创建的不是同一个实例");
}
}
}
工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,
达到提高灵活性的目的。
况下你可以考虑使用工厂模式:
1) 当客户程序不需要知道要使用对象的创建过程。
2) 客户程序使用的对象存在变动的可能,或者根本就不知道使用哪一个具体的对象。
我觉得一般最有用的就两种:静态工厂方法模式和抽象工厂模式
凡是出现了大量的产品需要创建,并且具有共同的接口时,可以通过工厂方法模式进行创建。
创建二者的共同接口:
1. public interface Sender {
2. public void Send();
3. }
其次,创建实现类:
1. public class MailSender implements Sender {
2. @Override
3. public void Send() {
4. System.out.println("this is mailsender!");
5. }
6. }
1. public class SmsSender implements Sender {
2.
3. @Override
4. public void Send() {
5. System.out.println("this is sms sender!");
6. }
7. }
创建工厂类
1. public class SendFactory {
2.
3. public static Sender produceMail(){
4. return new MailSender();
5. }
6.
7. public static Sender produceSms(){
8. return new SmsSender();
9. }
10.}
测试
1. public class FactoryTest {
2.
3. public static void main(String[] args) {
4. Sender sender = SendFactory.produceMail();
5. sender.Send();
6. }
7. }
静态工厂方法模式,将方法置为静态的,不需要创建实例,直接调用即可。
工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则。
创建二者的共同接口:
4. public interface Sender {
5. public void Send();
6. }
其次,创建实现类:
7. public class MailSender implements Sender {
8. @Override
9. public void Send() {
10. System.out.println("this is mailsender!");
11. }
12. }
8. public class SmsSender implements Sender {
9.
10. @Override
11. public void Send() {
12. System.out.println("this is sms sender!");
13. }
14.}
上面都一样
提供一个工厂接口:
1. public interface Provider {
2. public Sender produce(); //该函数返回类型是Sender
3. }
两个工厂类:
[java] view plaincopy
1. public class SendMailFactory implements Provider {
2.
3. @Override
4. public Sender produce(){
5. return new MailSender();
6. }
7. }
[java] view plaincopy
1. public class SendSmsFactory implements Provider{
2.
3. @Override
4. public Sender produce() {
5. return new SmsSender();
6. }
7. }
测试类:
1. public class Test {
2.
3. public static void main(String[] args) {
4. Provider provider = new SendMailFactory();
5. Sender sender = provider.produce();
6. sender.Send();
7. }
8. }
抽象工厂模式,创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。(比如这里如果想添加一个PicSender的功能,只需要先增加一个public class PicSender implements Sender 这个实现类,然后 增加一个工厂类public class SendPicFactory implements Provider。 而不需要修改原来的工厂))
用的时候,先new一个工厂类,实现Provider接口,再用这个工厂中的自带的函数创建一个Sender类,就OK了
这里提一下
享元模式(Flyweight)
享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用。
当一个客户端请求时,工厂需要检查当前对象池中是否有符合条件的对象,如果有,就返回已经存在的对象,如果没有,则创建一个新对象。节省了空间,减少了实例的数量。是本地内存资源节省方案。
具体怎么就不说了。对分布式可能没啥用。对单机提升性能可能有用。
建造者模式
将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
[构建与表示分离,同构建不同表示]
与抽象工厂的区别:在建造者模式里,有个指导者,由指导者来管理建造者,用户是与指导者联系的,指导者联系建造者最后得到产品。即建造模式可以强制实行一种分步骤进行的建造过程。
简单地说,就好象我要一座房子住,可是我不知道怎么盖(简单的砌墙,层次较低),也不知道怎么样设计(建几个房间,几个门好看,层次较高),于是我需要找一帮民工,他们会砌墙,还得找个设计师,他知道怎么设计,我还要确保民工听设计师的领导,而设计师本身也不干活,光是下命令,这里砌一堵墙,这里砌一扇门,这样民工开始建设,最后,我可以向民工要房子了。在这个过程中,设计师是什么也没有,除了他在脑子里的设计和命令,所以要房子也是跟民工要,记住了!
1,定义工人接口,就是能够完成建造房子任务的人的通用要求。
java 代码
1. // 工人接口,定义了各个工人所要进行的工所作。他们负责进行具体部件如窗户,地板的建造。
2. // 同时因为房子是民工建的,因此建设完成后由他把房子递交回房主
3. public interface Builder {
4.
5. public void makeWindow();
6.
7. public void makeFloor();
8.
9. public Room getRoom();
10. }
2,定义设计师,他的职责是指挥房主指派给他的工人按照自己的设计意图建造房子。
java 代码
1. // 设计师。他知道房子应该怎么设计,但他不会自己去建造,而是指挥民工按一定流程去建造。
2. public class Designer {
3. private Builder builder;
4. Public Designer(Builder builder){
5. this.builder = builder;
6. }
7. // 指挥民工进行工作
8. public void order(Builder builder) {
9. builder.makeWindow();
10. builder.makeFloor();
11. }
12. }
3,民工,他负责具体事物的实施。
1. // 民工。负责进行具体部件如窗户,地板的建造。
2. //同时因为房子是民工建的,因此建设完成后由他把房子递交回房主
3. public class Mingong implements Builder {
4. private String window="";
5. private String floor="";
6.
7. public void makeWindow() {
8. window=new String("window");
9. }
10.
11. public void makeFloor(){
12. floor=new String("floor");
13. }
14.
15. // 回交房子给房主
16. public Room getRoom() {
17. if((!window.equals(""))&&(!floor.equals(""))) {
18. System.out.println("room ready!");
19. return new Room();
20. }
21. else return null;
22. }
23. }
4,房主,就是雇人,收房。
1.
2. // 房主。房主的任务就是聘请一个民工,一个设计师,同时把民工给设计师指挥,督促设计师开展工作。最后从民工手上收房。
3. public class Client {
4.
5. public static void main(String[] args) {
6. Builder mingong = new Mingong();
7. Designer designer = new Designer();
8. designer.order(mingong);
9. mingong.getRoom();
10. }
11. }
想了想,如果把getRoom()方法放到Director里面,那不就成了类Proxy模式了吗?
还是以上面的这个建房的场景说事吧。如果我向Director要房子,那么我就不知道他是否用我雇用的民工来干活,也许他私下又雇了其他的人,而不用我雇的人。也就是彻头彻尾的代理了。
之所以要向Mingong要,是为了保证东西是由我亲自选定并信任的人来完成实际工作的。
原型模式
有的时候,我们需要一个实例时,并且,当创建一个实例的过程比较复杂或者说是昂贵时,
比如,创建实例的构造函数非常的复杂,在执行这个构造函数时会消耗较长的时间,
同时呢,这个构造函数中的一些信息又没有什么变化
(也就是说创建第一个实例时初始化信息是这样的,创建第二个实例时初始化信息还是还是这样的),
那么直接使用 new 来创建这样一个实例就显得太昂贵了,
最好的解决方法,并不是使用 new 来实例化一个对象,
在Java中,复制对象是通过clone()实现的,先创建一个原型类:
1. public class Prototype implements Cloneable {
2.
3. public Object clone() throws CloneNotSupportedException {
4. Prototype proto = (Prototype) super.clone();
5. return proto;
6. }
7. }
很简单,一个原型类,只需要实现Cloneable接口,覆写clone方法,此处clone方法可以改成任意的名称
// loneable接口并覆盖clone()方法
class ConcretePrototype01 implements Cloneable {
private String name;
public ConcretePrototype01(String name) { //构造函数
this.name = name;
}
public void getName() {
System.out.println(name);
}
//覆盖Object基类中的clone()方法,并扩大该方法的访问权限,具体化返回本类型
public ConcretePrototype01 clone() { //返回类型是本类
ConcretePrototype01 proto = null; //新建一个本类的对象
proto = (ConcretePrototype01) super.clone();
//如果不加 (ConcretePrototype01) ,则是object类型
return proto; //返回这个本类
}
//测试类
public class Client {
public static void main(String[] args) {
ConcretePrototype01 prototype01 = new ConcretePrototype01("蚂蚁 ...");
prototype01.getName();
//通过clone获得一个拷贝
ConcretePrototype01 fromClone01 = prototype01.clone();
fromClone01.getName();
}
}
适配器模式
比如,在一个画图的小程序中,你已经实现了绘制点、直线、方块等图形的功能。而且为
了让客户程序在使用的时候不用去关心它们的不同,还使用了一个抽象类来规范这些图形的接
口。现在你要来实现圆的绘制,这时你发现在系统其他的地方(textcircle)已经有了绘制圆的实现。在你庆
幸之余,发现系统中已有的方法和你在抽象类中规定的方法名称不一样!这可怎么办?修改绘
制圆的方法名,就要去修改所有使用它的地方;修改你的抽象类的方法名,也要去修改所有图形的实现方法以及已有的引用。还有其它的方法没有?那就是适配器模式了。可以看出使用适配器模式是为了在面向接口编程中更好的复用。
class Circle extends Shape
{
//这里引用了 TextCircle
private TextCircle tc;
public Circle ()
{
tc= new TextCircle(); //初始化
}
void public display()
{
tc.displayIt(); //在规定的方法里面调用 TextCircle 原来的方法
}
}
然后我们就能用
Circle mycircle = new circle();
Circle.display();
了
代理模式
说白了就是,在一些情况下客户不想或者不能直接引用一个对象,而代理对象可以在客户和目标对象之间起到中介作用,去掉客户不能看到的内容和服务或者增添客户需要的额外服务。
其实每个模式名称就表明了该模式的作用,代理模式就是多一个代理类出来,替原对象进行一些操作,比如我们在租房子的时候回去找中介,为什么呢?因为你对该地区房屋的信息掌握的不够全面,希望找一个更熟悉的人去帮你做,此处的代理就是这个意思。再如我们有的时候打官司,我们需要请律师,因为律师在法律方面有专长,可以替我们进行操作,表达我们的想法。
根据上文的阐述,代理模式就比较容易的理解了,我们看下代码:
[java] view plaincopy
1. public interface Sourceable {
2. public void method();
3. }
[java] view plaincopy
1. public class Source implements Sourceable {
2.
3. @Override
4. public void method() {
5. System.out.println("the original method!");
6. }
7. }
[java] view plaincopy
1. public class Proxy implements Sourceable {
2.
3. private Source source;
4. public Proxy(){
5. super();
6. this.source = new Source();
7. }
8. @Override
9. public void method() {
10. before();
11. source.method();
12. atfer();
13. }
14. private void atfer() {
15. System.out.println("after proxy!");
16. }
17. private void before() {
18. System.out.println("before proxy!");
19. }
20. }
测试类:
[java] view plaincopy
1. public class ProxyTest {
2.
3. public static void main(String[] args) {
4. Sourceable source = new Proxy();
5. source.method();
6. }
7.
8. }
输出:
before proxy!
the original method!
after proxy!
代理模式的应用场景:
如果已有的方法在使用的时候需要对原有的方法进行改进,此时有两种办法:
1、修改原有的方法来适应。这样违反了“对扩展开放,对修改关闭”的原则。
2、就是采用一个代理类调用原有的方法,且对产生的结果进行控制。这种方法就是代理模式。
使用代理模式,可以将功能划分的更加清晰,有助于后期维护!
代理模式和适配器模式的差别:
两者的主要区别在于代理模式应用的情况是不改变接口命名的,而且是对已有接口功能的一种控制;而适配器模式则强调接口转换。
外观模式
外观是一个能为子系统和客户提供简单接口的类。当正确的应用外观,客户不再直接和子系统中的类交互,而是与外观交互。外观承担与子系统中类交互的责任。实际上,外观是子系统与客户的接口,这样外观模式降低了子系统和客户的耦合度 。外观模式意图:为子系统中的一组接口提供一个一致的接口。这个接口定义了一个高层接口,这个接口使得这一子系统更加容易使用
我们先看下实现类:
[java] view plaincopy
1. public class CPU {
2.
3. public void startup(){
4. System.out.println("cpu startup!");
5. }
6.
7. public void shutdown(){
8. System.out.println("cpu shutdown!");
9. }
10. }
[java] view plaincopy
1. public class Memory {
2.
3. public void startup(){
4. System.out.println("memory startup!");
5. }
6.
7. public void shutdown(){
8. System.out.println("memory shutdown!");
9. }
10. }
[java] view plaincopy
1. public class Disk {
2.
3. public void startup(){
4. System.out.println("disk startup!");
5. }
6.
7. public void shutdown(){
8. System.out.println("disk shutdown!");
9. }
10. }
[java] view plaincopy
1. public class Computer {
2. private CPU cpu;
3. private Memory memory;
4. private Disk disk;
5.
6. public Computer(){
7. cpu = new CPU();
8. memory = new Memory();
9. disk = new Disk();
10. }
11.
12. public void startup(){
13. System.out.println("start the computer!");
14. cpu.startup();
15. memory.startup();
16. disk.startup();
17. System.out.println("start computer finished!");
18. }
19.
20. public void shutdown(){
21. System.out.println("begin to close the computer!");
22. cpu.shutdown();
23. memory.shutdown();
24. disk.shutdown();
25. System.out.println("computer closed!");
26. }
27. }
User类如下:
[java] view plaincopy
1. public class User {
2.
3. public static void main(String[] args) {
4. Computer computer = new Computer();
5. computer.startup();
6. computer.shutdown();
7. }
8. }
输出:
start the computer!
cpu startup!
memory startup!
disk startup!
start computer finished!
begin to close the computer!
cpu shutdown!
memory shutdown!
disk shutdown!
computer closed!
桥接模式
桥接模式的做法是把变化部分抽象出来,使变化部分与主类分离开来,从而将多个维度的变化彻底分离。最后,提供一个管理类来组合不同维度上的变化,通过这种组合来满足业务的需要。
桥接模式的意图是:将抽象与抽象方法的实现相互分离来实现解耦,以便二者可以相互独立的变化
。
下面举个例子说明:
1.现在水果里有 苹果和橘子
2.人分为 男人和女人
3.人都可以吃水果,所以有 2*2 = 4 中情况
如果我们不用桥接模式,那么实现这么多情况就需要一个一个的去实现,也就是4类,那么我们使用桥接模式呢?答案是:2类。这在条件和实现比较多的情况下优势会更明显。
//以下接口代表面条是否添加辣椒(该接口代表了面条在辣味风格这个维度上的变化)
public interface Peppery{
String style();
}
//实现类代表辣椒的风格
public class PepperyStyle implements Peppery{
public String style(){
return "辣味";
}
}
public class PlainStyle implements Peppery{
public String style(){
return "清淡" ;
}
}
/^
对于系统而言,辣味风格这个维度上的变化是固定的,程序必须面对的,程序使用桥接模式将辣味风格这个维度的变化分离出来了,避免与牛肉、猪肉材料风格这个维度的变化耦合在一起。
接下来是AbstractNoodle抽象类,其本身可以包含很多实现类,不同实现类则代表了面条在材料风格这个维度上的变化。
*/
public abstract class AbstractNoodle{
protected Peppery style;
public AbstractNoodle(Peppery style){
this.style = style;
}
public abstract void eat();
}
//这样就可以完成辣味风格、材料风格两个维度上变化的组合
/*
AbstractNoodle抽象类可看做是一个桥梁,它被用来“桥接”面条的材料风格的改变与辣味风格的改变,使面条的特殊属性得到无绑定的扩充。
*/
//以下是猪肉面实现类
public class PorkNoodle extends AbstractNoodle{
public PorkyNodle(Peppery style){
super(style);
}
public void eat(){
System.out.println( "猪肉面条的口味:"+super.style.style());
}
}
//测试类
public class Test{
public static void main(String[] args){
AbstractNoodle noodle = new PorkNoodle(new PepperyStyle());
noodle.eat();
AbstractNoodle noodle = new PorkNoodle(new PlainStyle());
noodle.eat();
}
}
策略模式
策略模式是对算法的封装,它把算法的责任和算法本身分割开,委派给不同的对象管理。策略模式通常把一个系列的算法封装到一系列的策略类里面,作为一个抽象策略类的子类。
)策略模式仅仅封装算法,提供新算法插入到已有系统中,以及老算法从系统中“退休”的方便,策略模式并不决定在何时使用何种算法,算法的选择由客户端来决定。这在一定程度上提高了系统的灵活性,但是客户端需要理解所有具体策略类之间的区别,以便选择合适的算法,这也是策略模式的缺点之一,在一定程度上增加了客户端的使用难度。
责任链(Chain of Responsibility)模式 :责任链模式是对象的行为模式。使多个对象都有机会处理请求,从而避免请求的发送者和接受者直接的耦合关系。将这些对象连成一条链,沿着这条链传递该请求,直到有一个对象处理它为止。
那么我们再模拟一下OA系统中请假审批流程,假如员工直接上司为小组长,小组长直接上司项目经理,项目经理直接上司部门经理,部门经理直接上司总经理。公司规定请假审批如下:
请假时间为t,时间单位day,简写d:
t< 0.5d,小组长审批;
t>=0.5d,t<2,项目经理审批;
t>=2,t<5部门经理审批;
t>=5总经理审批;
可以看出肯定需要if else。
责任链模式的优点是调用者不需知道具体谁来处理请求,也不知道链的具体结构,降低了节点域节点的耦合度;每个角色处理自己能处理的请求,不能处理则(交给)访问它的下家。
观察者模式
观察者模式很好理解,类似于邮件订阅和RSS订阅,当我们浏览一些博客或wiki时,经常会看到RSS图标,就这的意思是,当你订阅了该文章,如果后续有更新,会及时通知你。其实,简单来讲就一句话:当一个对象变化时,其它依赖该对象的对象都会收到通知,并且随着变化!对象之间是一种一对多的关系。
一个Observer接口:
[java] view plaincopy
1. public interface Observer {
2. public void update();
3. }
两个实现类:
[java] view plaincopy
1. public class Observer1 implements Observer {
2.
3. @Override
4. public void update() {
5. System.out.println("observer1 has received!");
6. }
7. }
[java] view plaincopy
1. public class Observer2 implements Observer {
2.
3. @Override
4. public void update() {
5. System.out.println("observer2 has received!");
6. }
7.
8. }
Subject接口及实现类:
[java] view plaincopy
1. public interface Subject {
2.
3. /*增加观察者*/
4. public void add(Observer observer);
5.
6. /*删除观察者*/
7. public void del(Observer observer);
8.
9. /*通知所有的观察者*/
10. public void notifyObservers();
11.
12. /*自身的操作*/
13. public void operation();
14. }
[java] view plaincopy
1. public abstract class AbstractSubject implements Subject {
2.
3. private Vector<Observer> vector = new Vector<Observer>();
4. @Override
5. public void add(Observer observer) {
6. vector.add(observer);
7. }
8.
9. @Override
10. public void del(Observer observer) {
11. vector.remove(observer);
12. }
13.
14. @Override
15. public void notifyObservers() {
16. Enumeration<Observer> enumo = vector.elements();
17. while(enumo.hasMoreElements()){
18. enumo.nextElement().update();
19. }
20. }
21. }
[java] view plaincopy
1. public class MySubject extends AbstractSubject {
2.
3. @Override
4. public void operation() { //操作分两步
5. System.out.println("update self!"); //1.自己更新
6. notifyObservers(); //2 通知所有观察者
7. }
8.
9. }
测试类:
[java] view plaincopy
1. public class ObserverTest {
2.
3. public static void main(String[] args) {
4. Subject sub = new MySubject();
5. sub.add(new Observer1());
6. sub.add(new Observer2());
7.
8. sub.operation();
9. }
10.
11. }
输出:
update self!
observer1 has received!
observer2 has received!