(JavaSE高级)三、Java 中的设计模式

1. 你所知道的设计模式有哪些

Java 中一般认为有 23 种设计模式,我们不需要所有的都会,但是其中常用的几种设计模式应该去掌握。下面列出了所有的设计模式。需要掌握的设计模式我单独列出来了,当然能掌握的越多越好。

总体来说设计模式分为三大类:

  • 创建型模式,共五种:工厂方法模式抽象工厂模式单例模式建造者模式、原型模式。
  • 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式
  • 行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

2. 单例设计模式

最好理解的一种设计模式,分为懒汉式和饿汉式。

饿汉式:
1. public class Singleton {
2. // 直接创建对象
3. public static Singleton instance = new Singleton();
4.
5. // 私有化构造函数
6. private Singleton() {
7. }
8.
9. // 返回对象实例
10. public static Singleton getInstance() {
11. return instance;
12. }
13. }

优点:没有并发问题,只会产生一个Singleton对象;
缺点:类初始化时就创建了对象,可能整个运行过程都不会调用,占用内存。

懒汉式:

1. public class Singleton {
2. // 声明变量
3. private static volatile Singleton singleton = null;
4.
5. // 私有构造函数
6. private Singleton() {
7. }
8.
9. // 提供对外方法
10. public static Singleton getInstance() {
11. if (singleton == null) {
12. synchronized (Singleton.class) {
13. if (singleton == null) {
14. singleton = new Singleton();
15. }
16. }
17. }
18. return singleton;
19. }
20. }

优点:使用时才会创建对象,避免了占用内存的问题;
缺点:使用synchronized解决并发问题(如果不用synchronized会有并发问题),则会影响效率。

3. 工厂设计模式

工厂模式分为工厂方法模式抽象工厂模式

  • 工厂方法模式
    工厂方法模式分为三种:
  1. 普通工厂模式,就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。
  2. 多个工厂方法模式,是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。
  3. 静态工厂方法模式,将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。

普通工厂模式

1. public interface Sender {
2. public void Send();
3. }
4. public class MailSender implements Sender {
5.
6. @Override
7. public void Send() {
8. System.out.println("this is mail sender!");
9. }
10. }
11. public class SmsSender implements Sender {
12.
13. @Override
14. public void Send() {
15. System.out.println("this is sms sender!");
16. }
17. }
18. public class SendFactory {
19. public Sender produce(String type) {
20. if ("mail".equals(type)) {
21. return new MailSender();
22. } else if ("sms".equals(type)) {
23. return new SmsSender();
24. } else {
25. System.out.println("请输入正确的类型!");
26. return null;
27. }
28. }
29. }

多个工厂方法模式
该模式是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。

1. public class SendFactory {
2. public Sender produceMail(){
3. return new MailSender();
4. }
5.
6. public Sender produceSms(){
7. return new SmsSender();
8. }
9. }
10.
11. public class FactoryTest {
12. public static void main(String[] args) {
13. SendFactory factory = new SendFactory();
14. Sender sender = factory.produceMail();
15. sender.send();
16. }
17. }

静态工厂方法模式,将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。

1. public class SendFactory {
2. public static Sender produceMail(){ 
3. return new MailSender();
4. }
5.
6. public static Sender produceSms(){
7. return new SmsSender();
8. }
9. }
10.
11.
12. public class FactoryTest {
13. public static void main(String[] args) {
14. Sender sender = SendFactory.produceMail();
15. sender.send();
16. }
17. }
  • 抽象工厂模式
    工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修
    改,这违背了闭包原则,所以,从设计角度考虑,有一定的问题,如何解决?就用到抽象工厂模式,创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。
1. public interface Provider {
2. public Sender produce();
3. }
4. -------------------------------------------------------------------------------------
5. public interface Sender {
6. public void send();
7. }
8. -------------------------------------------------------------------------------------
9. public class MailSender implements Sender {
10.
11. @Override
12. public void send() {
13. System.out.println("this is mail sender!");
14. }
15. }
16. -------------------------------------------------------------------------------------
17. public class SmsSender implements Sender {
18.
19. @Override
20. public void send() {
21. System.out.println("this is sms sender!");
22. }
23. }
24. -------------------------------------------------------------------------------------
25. public class SendSmsFactory implements Provider {
26.
27. @Override
28. public Sender produce() {
29. return new SmsSender();
30. }
31. }
1. public class SendMailFactory implements Provider {
2.
3. @Override
4. public Sender produce() {
5. return new MailSender();
6. }
7. }
8. -------------------------------------------------------------------------------------
9. public class Test {
10. public static void main(String[] args) {
11. Provider provider = new SendMailFactory();
12. Sender sender = provider.produce();
13. sender.send();
14. }
15. }

4. 建造者模式(Builder)

工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性,其实建造者模式就是前面抽象工厂模式和最后的 Test 结合起来得到的。(意思大概是工厂模式每次只会返回一种特定的对象,但是使用建造者模式,可以返回多种实现同一接口的不同实现类对象,就像建大楼,我需要材料,工厂方法每次就只送砖或其它某一种材料;但是通过建造者模式送砖及其混泥土等一堆材料)

1. public class Builder {
2. private List<Sender> list = new ArrayList<Sender>();
3.
4. public void produceMailSender(int count) {
5. for (int i = 0; i < count; i++) {
6. list.add(new MailSender());
7. }
8. }
9.
10. public void produceSmsSender(int count) {
11. for (int i = 0; i < count; i++) {
12. list.add(new SmsSender());
13. }
14. }
15. }
1. public class TestBuilder {
2. public static void main(String[] args) {
3. Builder builder = new Builder();
4. builder.produceMailSender(10);
5. }
6. }

5. 适配器设计模式

适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式。

  • 类的适配器模式
1. public class Source {
2. public void method1() {
3. System.out.println("this is original method!");
4. }
5. }
6. -------------------------------------------------------------
7. public interface Targetable {
8. /* 与原类中的方法相同 */
9. public void method1();
10. /* 新类的方法 */
11. public void method2();
12. }
13. public class Adapter extends Source implements Targetable {
14. @Override
15. public void method2() {
16. System.out.println("this is the targetable method!");
17. }
18. }
19. public class AdapterTest {
20. public static void main(String[] args) {
21. Targetable target = new Adapter();
22. target.method1();
23. target.method2();
24. }
25. }
  • 对象的适配器模式
    基本思路和类的适配器模式相同,只是将 Adapter 类作修改,这次不继承 Source 类,而是持有 Source 类的实例,以达到解决兼容性的问题。
1. public class Wrapper implements Targetable {
2. private Source source;
3.
4. public Wrapper(Source source) {
5. super();
6. this.source = source;
7. }
8.
9. @Override
10. public void method2() {
11. System.out.println("this is the targetable method!");
12. }
13.
14. @Override
15. public void method1() {
16. source.method1();
17. }
18. }
19. --------------------------------------------------------------
20. public class AdapterTest {
21.
22. public static void main(String[] args) {
23. Source source = new Source();
24. Targetable target = new Wrapper(source);
25. target.method1();
26. target.method2();
27. }
28. }
  • 接口的适配器模式
    接口的适配器是这样的:有时我们写的一个接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,这明显有时比较浪费,因为并不是所有的方法都是我们需要的,有时只需要某一些,此处为了解决这个问题,我们引入了接口的适配器模式,借助于一个抽象类,该抽象类实现了该接口,实现了所有的方法,而我们不和原始的接口打交道,只和该抽象类取得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行。

6. 装饰模式(Decorator)

顾名思义,装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。

1. public interface Sourceable {
2. public void method();
3. }
4. ----------------------------------------------------
5. public class Source implements Sourceable {
6. @Override
7. public void method() {
8. System.out.println("the original method!");
9. }
10. }
11. ----------------------------------------------------
12. public class Decorator implements Sourceable {
13. private Sourceable source;
14. public Decorator(Sourceable source) {
15. super();
16. this.source = source;
17. }
18.
19. @Override
20. public void method() {
21. System.out.println("before decorator!");
22. source.method();
23. System.out.println("after decorator!");
24. }
25. }
26. ----------------------------------------------------
27. public class DecoratorTest {
28. public static void main(String[] args) {
29. Sourceable source = new Source();
30. Sourceable obj = new Decorator(source);
31. obj.method();
32. }
33. }

7. 策略模式(strategy)

策略模式定义了一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响到使用算法的客户。需要设计一个接口,为一系列实现类提供统一的方法,多个实现类实现该接口,设计一个抽象类(可有可无,属于辅助类),提供辅助函数。策略模式的决定权在用户,系统本身提供不同算法的实现,新增或者删除算法,对各种算法做封装。因此,策略模式多用在算法决策系统中,外部用户只需要决定用哪个算法即可。

1. public interface ICalculator {
2. public int calculate(String exp);
3. }
4. ---------------------------------------------------------
5. public class Minus extends AbstractCalculator implements ICalculator {
6.
7. @Override
8. public int calculate(String exp) {
9. int arrayInt[] = split(exp, "-");
10. return arrayInt[0] - arrayInt[1];
11. }
12. }
13. ---------------------------------------------------------
14. public class Plus extends AbstractCalculator implements ICalculator {
15.
16. @Override
17. public int calculate(String exp) {
18. int arrayInt[] = split(exp, "\\+");
19. return arrayInt[0] + arrayInt[1];
20. }
21. }
22. --------------------------------------------------------
23. public class AbstractCalculator {
24. public int[] split(String exp, String opt) {
25. String array[] = exp.split(opt);
26. int arrayInt[] = new int[2];
27. arrayInt[0] = Integer.parseInt(array[0]);
28. arrayInt[1] = Integer.parseInt(array[1]);
29. return arrayInt;
30. }
31. }
1. public class StrategyTest {
2. public static void main(String[] args) {
3. String exp = "2+8";
4. ICalculator cal = new Plus();
5. int result = cal.calculate(exp);
6. System.out.println(result);
7. }
8. }

8. 观察者模式(Observer)

观察者模式很好理解,类似于邮件订阅和 RSS 订阅,当我们浏览一些博客或 wiki 时,经常会看到 RSS 图标,就这的意思是,当你订阅了该文章,如果后续有更新,会及时通知你。其实,简单来讲就一句话:当一个对象变化时,其它依赖该对象的对象都会收到通知,并且随着变化!对象之间是一种一对多的关系。

1. public interface Observer {
2. public void update();
3. }
4.
5. public class Observer1 implements Observer {
6. @Override
7. public void update() {
8. System.out.println("observer1 has received!");
9. }
10. }
11.
12. public class Observer2 implements Observer {
13. @Override
14. public void update() {
15. System.out.println("observer2 has received!");
16. }
17. }
18.
19. public interface Subject {
20. /*增加观察者*/
21. public void add(Observer observer);
22.
23. /*删除观察者*/
24. public void del(Observer observer);
25. /*通知所有的观察者*/
1. public void notifyObservers();
2. 
3. /*自身的操作*/
4. public void operation();
5. }
6.
7. public abstract class AbstractSubject implements Subject {
8.
9. private Vector<Observer> vector = new Vector<Observer>();
10.
11. @Override
12. public void add(Observer observer) {
13. vector.add(observer);
14. }
15.
16. @Override
17. public void del(Observer observer) {
18. vector.remove(observer);
19. }
20.
21. @Override
22. public void notifyObservers() {
23. Enumeration<Observer> enumo = vector.elements();
24. while (enumo.hasMoreElements()) {
25. enumo.nextElement().update();
26. }
27. }
28. }
29.
30. public class MySubject extends AbstractSubject {
31.
32. @Override
33. public void operation() {
34. System.out.println("update self!");
35. notifyObservers();
36. }
37. }
38.
39. public class ObserverTest {
40. public static void main(String[] args) {
41. Subject sub = new MySubject();
42. sub.add(new Observer1());
43. sub.add(new Observer2());
44. sub.operation();
45. }
46. }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容