第8篇-JAVA面向对象-设计模式Ⅳ
- 每篇一句 : 想象是程序的创作之源
- 初学心得 : 平静的海洋练不出熟练的水手
- (笔者:JEEP/711)[JAVA笔记 | 时间:2017-04-11| JAVA面向对象 Ⅳ]
JAVA设计模式
什么是JAVA设计模式以及作用?
设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结
使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性,本篇将介绍10种JAVA常用设计模式
1.JAVA 设计模式 - 单例设计模式
单例模式是一种创建模式,这种模式只涉及一个单独的类,它负责创建自己的对象
该类确保只创建单个对象,这个类提供了一种访问其唯一对象的方法
单例模式目的是为了整个应用中有且只有一个实例,所有指向该类型实例的引用都指向这个实例
保证对象不能再类的外部被随意实例化,解决方法:将构造器进行私有化处理
保证类创建的过程发生在类的内部,还有保证在类的外部能拿到在类的内部初始化的对象单例模式类型:饿汉式单例,懒汉式单例
Singleton(饿汉模式)
饿汉模式,特点是程序加载类的时候比较慢,但运行时获得对象的速度比较快,它从加载到应用结束会一直占用资源
饿汉模式代码:1.class Singleton { 2. public class SingletonDemo { 3. public static void main(String[] args) { 4. Singleton1 s1 = Singleton1.getInstance(); 5. Singleton1 s2 = Singleton1.getInstance(); 6. System.out.println(s1==s2); //true 7. Singleton2 s3 = Singleton2.getInstance(); 8. Singleton2 s4 = Singleton2.getInstance(); 9. System.out.println(s3==s4); //true 10. } 11.} 12.class Singleton1{ 13. private static final Singleton1 instance = new Singleton1(); //在内部准备好一个对象 14. public static Singleton1 getInstance(){ 15. return instance; 16. } 17. private Singleton1(){} 18. public void show(){ 19. System.out.println("Singleton1"); 20. } 21.} 22.class Singleton2 { 23. private static Singleton2 instance; 24. //将instance传递到外部去 25. public static Singleton2 getInstance(){ 26. if(instance == null){ 27. instance = new Singleton2(); 28. } 29. return instance; 30. } 31. private Singleton2(){} 32.}Singleton(懒汉模式)
懒汉模式特点,程序是运行时获得对象的速度比较慢,但加载类的时候比较快
它在整个应用的生命周期只有一部分时间在占用资源
懒汉模式代码:1.class Singleton{ 2. private static Singleton instance = null; 3. public static Singleton getInstance(){// 将instance传递到外部去 4. if(instance == null){ 5. instance = new Singleton(); 6. } 7. return instance; 8. } 9. private Singleton(){} 10.} 11.public static Singleton2 getInstance(){ 12. if(instance == null){ 13. synchronized(Singleton2.class){ 14. if(instance == null){ 15. instance = new Singleton2(); 16. } 17. } 18. } 19. return instance; 20. }Singleton(饿汉模式) & Singleton(懒汉模式) 区别
(1)这两种模式对于初始化较快,占用资源少的轻量级对象来说,没有多大的性能差异,选择懒汉式或饿汉式都没有问题
但是对于初始化慢,占用资源多的重 量级对象来说,就会有比较明显的差别了
所以,对重量级对象应用饿汉模式,类加载时速度慢,但运行时速度快;懒汉模式则与之相反,类加载时速度快,但运行时第一次获得对象的速度慢
(2)从用户体验的角度来说,我们应该首选饿汉模式。我们愿意等待某个程序花较长的时间初始化,却不喜欢在程序运行时等待太久,给人一种反应迟钝的感觉,所以对于有重量级对象参与的单例模式,笔者推荐使用饿汉模式
2.JAVA设计模式 - 享元设计模式
在JAVA语言中,String类型就是使用了享元模式。String对象是final类型,对象一旦创建就不可改变
在JAVA中字符串常量都是存在常量池中的,JAVA会确保一个字符串常量在常量池中只有一个拷贝
String a=”abc”,其中”abc”就是一个字符串常量
享元模式代码:1.public class Test { 2. public static void main(String[] args) { 3. String a = "abc"; 4. String b = "abc"; 5. System.out.println(a == b); 6. } 7.}上面的例子中结果为:true ,这就说明a和b两个引用都指向了常量池中的同一个字符串常量”abc”
这样的设计避免了在创建N多相同对象时所产生的不必要的大量的资源消耗
3.JAVA设计模式 - 简单工厂设计模式
简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例
简单工厂模式是工厂模式家族中最简单实用的模式,工厂模式就是用来生成对象的
简单工厂设计模式作用:降低耦合简单工厂模式代码:
1.//手机标准 2.interface ICellPhone { 3. void sendMsg(); 4.} 5./* 小米手机 */ 6.class Millet implements ICellPhone { 7. public void sendMsg() { 8. } 9.} 10./* 华为手机 */ 11.class Huawei implements ICellPhone { 12. public void sendMsg() { 13. } 14.} 15./* 手机工厂 */ 16.class Factory { 17. public static ICellPhone getInstance(String type){ 18. ICellPhone phone = null; 19. if("millet".equalsIgnoreCase(type)){ 20. phone = new Millet(); 21. }else if("huawei".equalsIgnoreCase(type)){ 22. phone = new Huawei(); 23. } 24. return phone; 25. } 26.} 27.public class FactoryDemo { 28. public static void main(String[] args) { 29. ICellPhone p = Factory.getInstance("millet"); 30. } 31.}如果直接使用了被调用者对象,而且又有可能会变化,那这个代码的可扩展性和柔韧性就不是很强基于这样的问题,所有我们就提出把客户端(调用者)不直接跟要调用的对象产生依赖关系,这样在扩展性和柔韧性会好一些,加入中间人,来引入工厂模式调控,单独声明一个工厂类,属于被调用者这一边,简单工厂类只负责产生对象
4.JAVA设计模式 - 抽象工厂设计模式
抽象工厂模式与简单工厂模式的区别:
(1)抽象工厂模式是简单工厂方法模式的升级版本,它用来创建一组相关或者相互依赖的对象。它与简单工厂方法模式的区别就在于,简单工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则是针对的多个产品等级结构
(2)在编程中,通常一个产品结构,表现为一个接口或者抽象类,也就是说,简单工厂方法模式提供的所有产品都是衍生自同一个2接口或抽象类,而抽象工厂模式所提供的产品则是衍生自不同的接口或抽象类
(3)在抽象工厂模式中,有一个产品族的概念:所谓的产品族,是指位于不同产品等级结构中功能相关联的产品组成的家族。抽象工厂模式所提供的一系列产品就组成一个产品族;而工厂方法提供的一系列产品称为一个等级结构抽象工厂模式代码:
1.interface IProduct1 { 2. public void show(); 3. } 4. interface IProduct2 { 5. public void show(); 6. } 7. class Product1 implements IProduct1 { 8. public void show() { 9. System.out.println("这是1型产品"); 10. } 11. } 12. class Product2 implements IProduct2 { 13. public void show() { 14. System.out.println("这是2型产品"); 15. } 16. } 17. interface IFactory { 18. public IProduct1 createProduct1(); 19. public IProduct2 createProduct2(); 20. } 21. class Factory implements IFactory{ 22. public IProduct1 createProduct1() { 23. return new Product1(); 24. } 25. public IProduct2 createProduct2() { 26. return new Product2(); 27. } 28. } 29. public class Client { 30. public static void main(String[] args){ 31. IFactory factory = new Factory(); 32. factory.createProduct1().show(); 33. factory.createProduct2().show(); 34. } 35. }抽象工厂模式的优点:
抽象工厂模式除了具有简单工厂方法模式的优点外,最主要的优点就是可以在类的内部对产品族进行约束,所谓的产品族,一般或多或少的都存在一定的关联,抽象工厂模式就可以在类内部对产品族的关联关系进行定义和描述,而不必专门引入一个新的类来进行管理
抽象工厂模式的缺点:
产品族的扩展将是一件十分费力的事情,假如产品族中需要增加一个新的产品,则几乎所有的工厂类都需要进行修改,所以使用抽象工厂模式时,对产品等级结构的划分是非常重要的
适用场景
当需要创建的对象是一系列相互关联或相互依赖的产品族时,便可以使用抽象工厂模式,说的更明白一点,就是一个继承体系中,如果存在着多个等级结构(即存在着多个抽象类),并且分属各个等级结构中的实现类之间存在着一定的关联或者约束,就可以使用抽象工厂模式,假如各个等级结构中的实现类之间不存在关联或约束,则使用多个独立的工厂来对产品进行创建,则更合适一点
5.JAVA设计模式 - 装饰设计模式
装饰模式在不链接其结构的情况下向现有对象添加新功能,它是一种结构型模式,因为它充当现有类的包装器
装饰模式创建一个装饰器类来包装原始类并提供其他功能
装饰模式代码:1.interface Printer { 2. void print(); 3.} 4. 5.class PaperPrinter implements Printer { 6. @Override 7. public void print() { 8. System.out.println("Paper Printer"); 9. } 10.} 11. 12.class PlasticPrinter implements Printer { 13. @Override 14. public void print() { 15. System.out.println("Plastic Printer"); 16. } 17.} 18. 19.abstract class PrinterDecorator implements Printer { 20. protected Printer decoratedPrinter; 21. public PrinterDecorator(Printer d){ 22. this.decoratedPrinter = d; 23. } 24. public void print(){ 25. decoratedPrinter.print(); 26. } 27.} 28. 29.class Printer3D extends PrinterDecorator { 30. public Printer3D(Printer decoratedShape) { 31. super(decoratedShape); 32. } 33. @Override 34. public void print() { 35. System.out.println("3D."); 36. decoratedPrinter.print(); 37. } 38.} 39. 40.public class Main { 41. public static void main(String[] args) { 42. Printer plasticPrinter = new PlasticPrinter(); 43. Printer plastic3DPrinter = new Printer3D(new PlasticPrinter()); 44. Printer paper3DPrinter = new Printer3D(new PaperPrinter()); 45. plasticPrinter.print(); 46. plastic3DPrinter.print(); 47. paper3DPrinter.print(); 48. } 49.}
6.JAVA设计模式 - 观察者设计模式
定义对象间一种一对多的依赖关系,使得当每一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新
观察者模式中,包括以下四个角色:
(1)被观察者:类中有一个用来存放观察者对象的Vector容器(之所以使用Vector而不使用List,是因为多线程操作时,Vector在是安全的,而List则是不安全的),这个Vector容器是被观察者类的核心,另外还有三个方法:attach方法是向这个容器中添加观察者对象;detach方法是从容器中移除观察者对象;notify方法是依次调用观察者对象的对应方法。这个角色可以是接口,也可以是抽象类或者具体的类,因为很多情况下会与其他的模式混用,所以使用抽象类的情况比较多
(2)观察者:观察者角色一般是一个接口,它只有一个update方法,在被观察者状态发生变化时,这个方法就会被触发调用
(3)具体的被观察者:使用这个角色是为了便于扩展,可以在此角色中定义具体的业务逻辑
(4)具体的观察者:观察者接口的具体实现,在这个角色中,将定义被观察者对象状态发生变化时所要处理的逻辑观察者模式代码实现:
1.abstract class Subject { 2. private Vector obs = new Vector(); 3. public void addObserver(Observer obs){ 4. this.obs.add(obs); 5. } 6. public void delObserver(Observer obs){ 7. this.obs.remove(obs); 8. } 9. protected void notifyObserver(){ 10. for(Observer o: obs){ 11. o.update(); 12. } 13. } 14. public abstract void doSomething(); 15. } 16. class ConcreteSubject extends Subject { 17. public void doSomething(){ 18. System.out.println("被观察者事件"); 19. this.notifyObserver(); 20. } 21. } 22. interface Observer { 23. public void update(); 24. } 25. class ConcreteObserver1 implements Observer { 26. public void update() { 27. System.out.println("观察者1收到信息进行处理"); 28. } 29. } 30. class ConcreteObserver2 implements Observer { 31. public void update() { 32. System.out.println("观察者2收到信息进行处理"); 33. } 34. } 35. public class Client { 36. public static void main(String[] args){ 37. Subject sub = new ConcreteSubject(); 38. sub.addObserver(new ConcreteObserver1()); //添加观察者1 39. sub.addObserver(new ConcreteObserver2()); //添加观察者2 40. sub.doSomething(); 41. } 42. }观察者模式的优点:观察者与被观察者之间是属于轻度的关联关系,并且是抽象耦合的,这样对于两者来说都比较容易进行扩展,观察者模式是一种常用的触发机制,它形成一条触发链,依次对各个观察者的方法进行处理,但同时,这也算是观察者模式一个缺点,由于是链式触发,当观察者比较多的时候,性能问题是比较令人担忧的。并且,在链式结构中,比较容易出现循环引用的错误,造成系统假死
7.JAVA设计模式 - 适配器设计模式
在JAVA设计模式中,适配器模式作为两个不兼容接口之间的桥梁
通过使用适配器模式,可以统一两个不兼容的接口
适配器设计模式代码:1.//适配器模式 2.public class Shipeiqi{ 3. public static void main(String [] args){ 4. ModificationWindow i = new ModificationWindow(); 5. i.close(); 6. } 7.} 8. 9.//定义一个接口 10.interface IWindow{ 11. void man();//只声明方法, 12. void min();//只声明方法 13. void close();//只声明方法, 14.} 15. 16.//定义一个抽象实现 17.abstract class MyWindow implements IWindow{ 18. public void man(){}; 19. public void min(){}; 20. public void close(){}; 21.} 22. 23.//定义一个类继承接口 24.class ModificationWindow extends MyWindow{ 25. public void close(){ 26. System.out.println("我将实现关闭功能"); 27. } 28.}
8.JAVA设计模式 - 静态代理设计模式
静态代理设计模式:如生活当中的代理,代驾,代购,待产…
代理模式(Proxy):为其他对象提供一种代理以控制对这个对象的访问
代理模式,白了就是“真实对象”的代表,在访问对象时引入一定程度的间接性,因为这种间接性可能附和多种用途(权限的控制、对象的访问、远程的代理)
代理类第一先要实现接口,第二要维护一个代理的对象,代理对象也是通过主题接口声明的,再通过构造方法或者get,set传值,这就是静态代理静态代理模式代码:
1.//静态代理设计模式 2. 3.//声明一个类 4.public class Jingtai{ 5. //主方法 6. public static void main(String [] args){ 7. Person p = new Person("老王 ");//实例化Person对象 8. //创建代理对象,并把被代理对象传进来,p传给了Matchmaker类中的target 9. Matchmaker m = new Matchmaker(p); //需要一个代理对象,把被代理对象传过来 10. m.miai();//真正执行调用的是 11. } 12.} 13. 14.//定义一个接口 主题接口 15.interface Subject{ 16. public void miai();//抽象方法 17.} 18. 19.//代理相当于代理接口的方法 20.//定义实现一个接口,相当于被代理类 21.class Person implements Subject{ 22. private String name;//定义私有的属性 23. //一参构造方法 24. public Person(String name){ 25. this.name = name; 26. } 27. 28. //定义实现方法 29. public void miai(){ 30. System.out.println(name+"正在相亲中...");//输出 31. } 32.} 33. 34.//定义一个代理类,代理的是过程,实现以后,是为了实现方法,需要重写方法 35.class Matchmaker implements Subject{ 36. private Subject target;//要代理的目标对象,通过定义一个代理的对象实现的接口,代理一个对象或者说一个属性, 37. //可以用构造方法传值,也可以用get,set方法传值 38. //构造方法传值 39. public Matchmaker(Subject target){ 40. this.target = target; 41. } 42. 43. //相亲之前要做的事情,封装起来 44. private void before(){ 45. System.out.println("为代理人,匹配如意郎君"); 46. } 47. 48. //相亲之后要做的事情 49. private void after(){ 50. System.out.println("本次相亲结束."); 51. } 52. 53. //需要重写方法,相亲的方法 54. public void miai(){ 55. before();//调用相亲之前要做的事情 56. target.miai();//真正执行相亲的方法,调用需要目标对象 57. after();//相亲之后要做的事情 58. } 59.}
9.JAVA设计模式 - 迭代器设计模式
迭代器模式以顺序方式访问集合对象的元素
迭代器模式代码:1.interface Iterator { 2. public boolean hasNext(); 3. public Object next(); 4.} 5.class LetterBag { 6. public String names[] = {"R" , "J" ,"A" , "L"}; 7. public Iterator getIterator() { 8. return new NameIterator(); 9. } 10. class NameIterator implements Iterator { 11. int index; 12. @Override 13. public boolean hasNext() { 14. if(index < names.length){ 15. return true; 16. } 17. return false; 18. } 19. @Override 20. public Object next() { 21. if(this.hasNext()){ 22. return names[index++]; 23. } 24. return null; 25. } 26. } 27.} 28.public class Main { 29. public static void main(String[] args) { 30. LetterBag bag = new LetterBag(); 31. for(Iterator iter = bag.getIterator(); iter.hasNext();){ 32. String name = (String)iter.next(); 33. System.out.println("Name : " + name); 34. } 35. } 36.}迭代器模式的优点:
简化了遍历方式,对于对象集合的遍历,还是比较麻烦的,对于数组或者有序列表,可以通过下坐标来取得,但用户需要在对集合了解很清楚的前提下,自行遍历对象,但是对于hash表来说,用户遍历起来就比较麻烦了,而引入了迭代器方法后,用户用起来就简单的多了,可以提供多种遍历方式,比如说对有序列表,我们可以根据需要提供正序遍历,倒序遍历两种迭代器,用户用起来只需要得到我们实现好的迭代器,就可以方便的对集合进行遍历了,封装性良好,用户只需要得到迭代器就可以遍历,而对于遍历算法则不用去关心
迭代器模式的缺点:
对于比较简单的遍历(像数组或者有序列表),使用迭代器方式遍历较为繁琐,像ArrayList,宁可愿意使用for循环和get方法来遍历集合
10.JAVA设计模式 - 生产者与消费者设计模式
生产者与消费者模式代码:
1.package cn.sc; 2./** 3.*生产者与消费者应用案例 4.*sleep与wait区别 5.*sleep让当前的线程进入休眠状态,让出cpu,让其他线程执行 6.*如果用同步的话,有对象锁的时候,是不会释放的,只能等待此线程使用完,才可以使用 7.*wait会释放对象锁,必须等待其他线程唤醒 8.*@author JEEP-711 9.* 10.*/ 11.public class ScXf { 12. public static void main(String[] args) { 13. Phones p = new Phones(null, null);//创建Phones对象 14. PhoneSc s = new PhoneSc(p);//创建PhoneSc对象 15. PhoneXf x = new PhoneXf(p);//创建PhoneXf对象 16. new Thread(s).start();//启动生产者线程 17. new Thread(x).start();//启动消费者线程 18. } 19.} 20. 21./** 22.* 手机生产者,单独的生产者,实现Runnable接口 23.* @author JEEP-711 24.* 25.*/ 26.class PhoneSc implements Runnable{ 27. private Phones phones; 28. public PhoneSc(Phones phones){ 29. this.phones = phones; 30. } 31. @Override 32. public void run() { 33. //不断地生产20份,生产的过程 34. for (int i = 0; i < 50; i++) { 35. if(i%2==0){ 36. phones.set("金立手机", "金立手机,中国造!"); 37. }else{ 38. phones.set("小米手机", "小米手机,为发烧而生!"); 39. } 40. } 41. } 42.} 43. 44./** 45. *手机消费者,顾客 46.*@author JEEP-711 47.* 48.*/ 49.class PhoneXf implements Runnable{ 50. private Phones phones; 51. public PhoneXf(Phones phones){ 52. this.phones = phones; 53. } 54. @Override 55. public void run() { 56. for (int i = 0; i < 50; i++) { 57. phones.get();//调用消费产品方法 58. } 59. } 60.} 61. 62./** 63.*产品的对象,生产的手机 64.*@author JEEP-711 65.* 66.*/ 67.class Phones{ 68. @Override 69. public String toString() { 70. return "Phones [name=" + name + ", content=" + content + "]"; 71. } 72. private String name; 73. private String content; 74. /**true表示可以生产,false表示可以消费 75.*作为标记,如何flag等于true表示可以生产,如何flag等于false表示不可生产 76.*如果flag等于false表示可以消费状态,可以取走,flag等于true表示不能取走 77.*解决重复值得问题 78.*/ 79. private boolean flag = true;//表示可以生产,false表示可以消费 80. //构造方法 81. public Phones(String name, String content) { 82. super(); 83. this.name = name; 84. this.content = content; 85. } 86. //取得名称方法 87. public String getName() { 88. return name; 89. } 90. //设置名称方法 91. public void setName(String name) { 92. this.name = name; 93. } 94. //取得内容方法 95. public String getContent() { 96. return content; 97. } 98. //设置内容方法 99. public void setContent(String content) { 100. this.content = content; 101. } 102. 103. /** 104. *通过同步,解决了取值错误问题 105. *@param name 106. *@param content 107. */ 108. //生产制造同步方法 109. public synchronized void set(String name, String content){ 110. if(!flag){ 111. try { 112. //调用该方法,当前线程进入等待池等待状态,没有指定时间, 113. //需要其他线程唤醒,释放对象锁,让出cpu 114. this.wait(); 115. } catch (InterruptedException e) { 116. e.printStackTrace(); 117. } 118. } 119. this.setName(name); 120. try { 121. Thread.sleep(300); 122. } catch (InterruptedException e) { 123. e.printStackTrace(); 124. } 125. this.setContent(content); 126. flag = false;//表示可以消费,取走 127. this.notify();//唤醒在该监视器上的一个线程 128. } 129. 130. //消费产品同步取值方法 131. public synchronized void get(){ 132. if(flag){ 133. try { 134. //调用该方法,当前线程进入等待池等待状态,没有指定时间, 135. //需要其他线程唤醒,释放对象锁,让出cpu 136. this.wait(); 137. } catch (InterruptedException e) { 138. e.printStackTrace(); 139. } 140. } 141. try { 142. Thread.sleep(300); 143. } catch (InterruptedException e) { 144. e.printStackTrace(); 145. } 146. System.out.println(this.getName()+":"+this.getContent()); 147. flag = true; 148. this.notify(); 149. } 150.}
初学(面向对象近阶段) Ⅳ 难点: ★★★★★★★
希望每一篇文章都能够对读者们提供帮助与提升,这乃是每一位笔者的初衷
感谢您的阅读 欢迎您的留言与建议
- 新浪官方微博: @中國熱點影視傳媒
- Github博客: https://github.com/jeep711/jeep711.github.io
- Blog Garden:http://www.cnblogs.com/JEEP711/
- W3C/Blog:http://www.w3cschool.cn/jeep711blog/
- CSDN/Blog:http://blog.csdn.net/jeep911
- 51CTO/Blog:http://jeep711.blog.51cto.com/
- 码云:http://git.oschina.net/JEEP711/jeep711.github.io
- 邮箱: [email protected],[email protected]