第8篇-JAVA面向对象-设计模式Ⅳ

第8篇-JAVA面向对象-设计模式Ⅳ

  • 每篇一句 : 想象是程序的创作之源
  • 初学心得 : 平静的海洋练不出熟练的水手
  • (笔者:JEEP/711)[JAVA笔记 | 时间:2017-04-11| JAVA面向对象 Ⅳ]

目录导航


JAVA设计模式

什么是JAVA设计模式以及作用?
设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结
使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性,本篇将介绍10种JAVA常用设计模式

1.JAVA 设计模式 - 单例设计模式

单例模式是一种创建模式,这种模式只涉及一个单独的类,它负责创建自己的对象
该类确保只创建单个对象,这个类提供了一种访问其唯一对象的方法
单例模式目的是为了整个应用中有且只有一个实例,所有指向该类型实例的引用都指向这个实例
保证对象不能再类的外部被随意实例化,解决方法:将构造器进行私有化处理
保证类创建的过程发生在类的内部,还有保证在类的外部能拿到在类的内部初始化的对象

单例模式类型:饿汉式单例,懒汉式单例

Singleton(饿汉模式)
饿汉模式,特点是程序加载类的时候比较慢,但运行时获得对象的速度比较快,它从加载到应用结束会一直占用资源
饿汉模式代码:

class Singleton {
       public class SingletonDemo {
  public static void main(String[] args) {
      Singleton1 s1 = Singleton1.getInstance();
      Singleton1 s2 = Singleton1.getInstance();
      System.out.println(s1==s2);  //true
      Singleton2 s3 = Singleton2.getInstance();
      Singleton2 s4 = Singleton2.getInstance();
      System.out.println(s3==s4);  //true
  }
}
class Singleton1{
  private static final Singleton1 instance = new Singleton1();  //在内部准备好一个对象  
  public static Singleton1 getInstance(){
      return instance;
  }
  private Singleton1(){}
  public void show(){
      System.out.println("Singleton1");
  }
}
class Singleton2 {
  private static Singleton2 instance;
  //将instance传递到外部去
  public static Singleton2 getInstance(){ 
      if(instance == null){
          instance = new Singleton2();
      }
      return instance;
  }
  private Singleton2(){}
}

Singleton(懒汉模式)
懒汉模式特点,程序是运行时获得对象的速度比较慢,但加载类的时候比较快
它在整个应用的生命周期只有一部分时间在占用资源
懒汉模式代码:

class Singleton{
  private static Singleton instance = null;
  public static Singleton getInstance(){// 将instance传递到外部去
      if(instance == null){
          instance = new Singleton();
      }
      return instance;
  }
  private Singleton(){}
}
public static Singleton2 getInstance(){
     if(instance == null){
        synchronized(Singleton2.class){
             if(instance == null){
                instance = new Singleton2(); 
              }
          }
     }     
   return instance;
  }

Singleton(饿汉模式) & Singleton(懒汉模式) 区别
(1)这两种模式对于初始化较快,占用资源少的轻量级对象来说,没有多大的性能差异,选择懒汉式或饿汉式都没有问题
但是对于初始化慢,占用资源多的重 量级对象来说,就会有比较明显的差别了
所以,对重量级对象应用饿汉模式,类加载时速度慢,但运行时速度快;懒汉模式则与之相反,类加载时速度快,但运行时第一次获得对象的速度慢
(2)从用户体验的角度来说,我们应该首选饿汉模式。我们愿意等待某个程序花较长的时间初始化,却不喜欢在程序运行时等待太久,给人一种反应迟钝的感觉,所以对于有重量级对象参与的单例模式,笔者推荐使用饿汉模式

2.JAVA设计模式 - 享元设计模式

在JAVA语言中,String类型就是使用了享元模式。String对象是final类型,对象一旦创建就不可改变
在JAVA中字符串常量都是存在常量池中的,JAVA会确保一个字符串常量在常量池中只有一个拷贝
String a=”abc”,其中”abc”就是一个字符串常量
享元模式代码:

public class Test {
   public static void main(String[] args) {
       String a = "abc";
       String b = "abc";
       System.out.println(a == b);
   }
}

上面的例子中结果为:true ,这就说明a和b两个引用都指向了常量池中的同一个字符串常量”abc”
这样的设计避免了在创建N多相同对象时所产生的不必要的大量的资源消耗

3.JAVA设计模式 - 简单工厂设计模式

简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例
简单工厂模式是工厂模式家族中最简单实用的模式,工厂模式就是用来生成对象的
简单工厂设计模式作用:降低耦合

简单工厂模式代码:

//手机标准
interface ICellPhone {
  void sendMsg();
}
/* 小米手机 */
class Millet implements ICellPhone {
  public void sendMsg() {
  }
}
/* 华为手机 */
class Huawei implements ICellPhone {
  public void sendMsg() {
  }
}
/* 手机工厂 */
class Factory {
  public static ICellPhone getInstance(String type){
      ICellPhone phone = null;
      if("millet".equalsIgnoreCase(type)){
          phone = new Millet();
      }else if("huawei".equalsIgnoreCase(type)){
          phone = new Huawei();
      }
      return phone;
  }
}
public class FactoryDemo {
  public static void main(String[] args) {
      ICellPhone p = Factory.getInstance("millet");
  }
}

如果直接使用了被调用者对象,而且又有可能会变化,那这个代码的可扩展性和柔韧性就不是很强基于这样的问题,所有我们就提出把客户端(调用者)不直接跟要调用的对象产生依赖关系,这样在扩展性和柔韧性会好一些,加入中间人,来引入工厂模式调控,单独声明一个工厂类,属于被调用者这一边,简单工厂类只负责产生对象

4.JAVA设计模式 - 抽象工厂设计模式

抽象工厂模式与简单工厂模式的区别:
(1)抽象工厂模式是简单工厂方法模式的升级版本,它用来创建一组相关或者相互依赖的对象。它与简单工厂方法模式的区别就在于,简单工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则是针对的多个产品等级结构
(2)在编程中,通常一个产品结构,表现为一个接口或者抽象类,也就是说,简单工厂方法模式提供的所有产品都是衍生自同一个2接口或抽象类,而抽象工厂模式所提供的产品则是衍生自不同的接口或抽象类
(3)在抽象工厂模式中,有一个产品族的概念:所谓的产品族,是指位于不同产品等级结构中功能相关联的产品组成的家族。抽象工厂模式所提供的一系列产品就组成一个产品族;而工厂方法提供的一系列产品称为一个等级结构

抽象工厂模式代码:

interface IProduct1 {
       public void show();
   }
   interface IProduct2 {
       public void show();
   }
   class Product1 implements IProduct1 {
       public void show() {
           System.out.println("这是1型产品");
       }
   }
   class Product2 implements IProduct2 {
       public void show() {
           System.out.println("这是2型产品");
       }
   }
   interface IFactory {
       public IProduct1 createProduct1();
       public IProduct2 createProduct2();
   }
   class Factory implements IFactory{
       public IProduct1 createProduct1() {
           return new Product1();
       }
       public IProduct2 createProduct2() {
           return new Product2();
       }
   }
   public class Client {
       public static void main(String[] args){
           IFactory factory = new Factory();
           factory.createProduct1().show();
           factory.createProduct2().show();
       }
   }

抽象工厂模式的优点
抽象工厂模式除了具有简单工厂方法模式的优点外,最主要的优点就是可以在类的内部对产品族进行约束,所谓的产品族,一般或多或少的都存在一定的关联,抽象工厂模式就可以在类内部对产品族的关联关系进行定义和描述,而不必专门引入一个新的类来进行管理
抽象工厂模式的缺点
产品族的扩展将是一件十分费力的事情,假如产品族中需要增加一个新的产品,则几乎所有的工厂类都需要进行修改,所以使用抽象工厂模式时,对产品等级结构的划分是非常重要的
适用场景
当需要创建的对象是一系列相互关联或相互依赖的产品族时,便可以使用抽象工厂模式,说的更明白一点,就是一个继承体系中,如果存在着多个等级结构(即存在着多个抽象类),并且分属各个等级结构中的实现类之间存在着一定的关联或者约束,就可以使用抽象工厂模式,假如各个等级结构中的实现类之间不存在关联或约束,则使用多个独立的工厂来对产品进行创建,则更合适一点

5.JAVA设计模式 - 装饰设计模式

装饰模式在不链接其结构的情况下向现有对象添加新功能,它是一种结构型模式,因为它充当现有类的包装器
装饰模式创建一个装饰器类来包装原始类并提供其他功能
装饰模式代码:

interface Printer {
  void print();
}

class PaperPrinter implements Printer {
  @Override
  public void print() {
     System.out.println("Paper Printer");
   }
}

class PlasticPrinter implements Printer {
  @Override
  public void print() {
     System.out.println("Plastic Printer");
   }
}

abstract class PrinterDecorator implements Printer {
  protected Printer decoratedPrinter;
  public PrinterDecorator(Printer d){
     this.decoratedPrinter = d;
  }
  public void print(){
     decoratedPrinter.print();
   }  
}

class Printer3D extends PrinterDecorator {
  public Printer3D(Printer decoratedShape) {
     super(decoratedShape);    
  }
  @Override
  public void print() {
    System.out.println("3D.");
    decoratedPrinter.print();         
   }
}

public class Main {
  public static void main(String[] args) {
     Printer plasticPrinter = new PlasticPrinter();
     Printer plastic3DPrinter = new Printer3D(new PlasticPrinter());
     Printer paper3DPrinter = new Printer3D(new PaperPrinter());
     plasticPrinter.print();
     plastic3DPrinter.print();
     paper3DPrinter.print();
   }
}

6.JAVA设计模式 - 观察者设计模式

定义对象间一种一对多的依赖关系,使得当每一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新
观察者模式中,包括以下四个角色
(1)被观察者:类中有一个用来存放观察者对象的Vector容器(之所以使用Vector而不使用List,是因为多线程操作时,Vector在是安全的,而List则是不安全的),这个Vector容器是被观察者类的核心,另外还有三个方法:attach方法是向这个容器中添加观察者对象;detach方法是从容器中移除观察者对象;notify方法是依次调用观察者对象的对应方法。这个角色可以是接口,也可以是抽象类或者具体的类,因为很多情况下会与其他的模式混用,所以使用抽象类的情况比较多
(2)观察者:观察者角色一般是一个接口,它只有一个update方法,在被观察者状态发生变化时,这个方法就会被触发调用
(3)具体的被观察者:使用这个角色是为了便于扩展,可以在此角色中定义具体的业务逻辑
(4)具体的观察者:观察者接口的具体实现,在这个角色中,将定义被观察者对象状态发生变化时所要处理的逻辑

观察者模式代码实现:

abstract class Subject {
       private Vector obs = new Vector();
       public void addObserver(Observer obs){
           this.obs.add(obs);
       }
       public void delObserver(Observer obs){
           this.obs.remove(obs);
       }
       protected void notifyObserver(){
          for(Observer o: obs){
               o.update();
           }
       }
       public abstract void doSomething();
   }
   class ConcreteSubject extends Subject {
       public void doSomething(){
           System.out.println("被观察者事件");
           this.notifyObserver();
       }
   }
   interface Observer {
       public void update();
   }
   class ConcreteObserver1 implements Observer {
       public void update() {
           System.out.println("观察者1收到信息进行处理");
       }
   }
   class ConcreteObserver2 implements Observer {
       public void update() {
           System.out.println("观察者2收到信息进行处理");
       }
   }
   public class Client {
       public static void main(String[] args){
           Subject sub = new ConcreteSubject();
           sub.addObserver(new ConcreteObserver1());  //添加观察者1
           sub.addObserver(new ConcreteObserver2());  //添加观察者2
           sub.doSomething();
       }
   }

观察者模式的优点:观察者与被观察者之间是属于轻度的关联关系,并且是抽象耦合的,这样对于两者来说都比较容易进行扩展,观察者模式是一种常用的触发机制,它形成一条触发链,依次对各个观察者的方法进行处理,但同时,这也算是观察者模式一个缺点,由于是链式触发,当观察者比较多的时候,性能问题是比较令人担忧的。并且,在链式结构中,比较容易出现循环引用的错误,造成系统假死

7.JAVA设计模式 - 适配器设计模式

在JAVA设计模式中,适配器模式作为两个不兼容接口之间的桥梁
通过使用适配器模式,可以统一两个不兼容的接口
适配器设计模式代码:

//适配器模式
public class Shipeiqi{
  public static void main(String [] args){
      ModificationWindow i = new ModificationWindow();
      i.close();  
   }
}

//定义一个接口
interface IWindow{
  void man();//只声明方法,
  void min();//只声明方法
   void close();//只声明方法,
}

//定义一个抽象实现
abstract class MyWindow implements IWindow{
  public void man(){};
  public void min(){};
  public void close(){};
}

//定义一个类继承接口
class ModificationWindow extends MyWindow{
  public void close(){
      System.out.println("我将实现关闭功能");
   }
}

8.JAVA设计模式 - 静态代理设计模式

静态代理设计模式:如生活当中的代理,代驾,代购,待产…
代理模式(Proxy):为其他对象提供一种代理以控制对这个对象的访问
代理模式,白了就是“真实对象”的代表,在访问对象时引入一定程度的间接性,因为这种间接性可能附和多种用途(权限的控制、对象的访问、远程的代理)
代理类第一先要实现接口,第二要维护一个代理的对象,代理对象也是通过主题接口声明的,再通过构造方法或者get,set传值,这就是静态代理

静态代理模式代码:

//静态代理设计模式

//声明一个类
public class Jingtai{
  //主方法
  public static void main(String [] args){
      Person p = new Person("老王 ");//实例化Person对象
      //创建代理对象,并把被代理对象传进来,p传给了Matchmaker类中的target
      Matchmaker m = new Matchmaker(p); //需要一个代理对象,把被代理对象传过来
      m.miai();//真正执行调用的是     
   }
}

//定义一个接口  主题接口
interface Subject{
  public void miai();//抽象方法
}

//代理相当于代理接口的方法
//定义实现一个接口,相当于被代理类
class Person implements Subject{
  private String name;//定义私有的属性
  //一参构造方法
  public Person(String name){
      this.name = name;
  }

  //定义实现方法
  public void miai(){
      System.out.println(name+"正在相亲中...");//输出
   }
}

//定义一个代理类,代理的是过程,实现以后,是为了实现方法,需要重写方法
class Matchmaker implements Subject{
  private Subject target;//要代理的目标对象,通过定义一个代理的对象实现的接口,代理一个对象或者说一个属性,
  //可以用构造方法传值,也可以用get,set方法传值
  //构造方法传值
  public Matchmaker(Subject target){
      this.target = target;
  }

  //相亲之前要做的事情,封装起来
  private void before(){
      System.out.println("为代理人,匹配如意郎君");
  }

  //相亲之后要做的事情
  private void after(){
      System.out.println("本次相亲结束.");  
  }

  //需要重写方法,相亲的方法
  public void miai(){
      before();//调用相亲之前要做的事情
      target.miai();//真正执行相亲的方法,调用需要目标对象
      after();//相亲之后要做的事情
  }
}

9.JAVA设计模式 - 迭代器设计模式

迭代器模式以顺序方式访问集合对象的元素
迭代器模式代码:

interface Iterator {
  public boolean hasNext();
  public Object next();
}
class LetterBag {
  public String names[] = {"R" , "J" ,"A" , "L"};
  public Iterator getIterator() {
     return new NameIterator();
  }
  class NameIterator implements Iterator {
     int index;
     @Override
     public boolean hasNext() {
        if(index < names.length){
           return true;
        }
        return false;
     }
     @Override
     public Object next() {
        if(this.hasNext()){
           return names[index++];
        }
        return null;
     }    
  }
}
public class Main {
  public static void main(String[] args) {
     LetterBag bag = new LetterBag();
     for(Iterator iter = bag.getIterator(); iter.hasNext();){
        String name = (String)iter.next();
        System.out.println("Name : " + name);
     }   
  }
}

迭代器模式的优点
简化了遍历方式,对于对象集合的遍历,还是比较麻烦的,对于数组或者有序列表,可以通过下坐标来取得,但用户需要在对集合了解很清楚的前提下,自行遍历对象,但是对于hash表来说,用户遍历起来就比较麻烦了,而引入了迭代器方法后,用户用起来就简单的多了,可以提供多种遍历方式,比如说对有序列表,我们可以根据需要提供正序遍历,倒序遍历两种迭代器,用户用起来只需要得到我们实现好的迭代器,就可以方便的对集合进行遍历了,封装性良好,用户只需要得到迭代器就可以遍历,而对于遍历算法则不用去关心
迭代器模式的缺点
对于比较简单的遍历(像数组或者有序列表),使用迭代器方式遍历较为繁琐,像ArrayList,宁可愿意使用for循环和get方法来遍历集合

10.JAVA设计模式 - 生产者与消费者设计模式

生产者与消费者模式代码:

package cn.sc;
/**
*生产者与消费者应用案例
*sleep与wait区别
*sleep让当前的线程进入休眠状态,让出cpu,让其他线程执行
*如果用同步的话,有对象锁的时候,是不会释放的,只能等待此线程使用完,才可以使用
*wait会释放对象锁,必须等待其他线程唤醒
*@author JEEP-711
*
*/
public class ScXf {
  public static void main(String[] args) {
      Phones p = new Phones(null, null);//创建Phones对象
      PhoneSc s = new PhoneSc(p);//创建PhoneSc对象 
      PhoneXf x = new PhoneXf(p);//创建PhoneXf对象
      new Thread(s).start();//启动生产者线程
      new Thread(x).start();//启动消费者线程
   }
}

/**
* 手机生产者,单独的生产者,实现Runnable接口
* @author JEEP-711
*
*/
class PhoneSc implements Runnable{
  private Phones phones;
  public PhoneSc(Phones phones){
      this.phones = phones;
  }
  @Override
  public void run() {
      //不断地生产20份,生产的过程
      for (int i = 0; i < 50; i++) {
          if(i%2==0){
              phones.set("金立手机", "金立手机,中国造!");
          }else{
              phones.set("小米手机", "小米手机,为发烧而生!");
          }
      }
   }
}

/**
 *手机消费者,顾客
*@author JEEP-711
*
*/
class PhoneXf implements Runnable{
  private Phones phones;
  public PhoneXf(Phones phones){
      this.phones = phones;
  }
  @Override
  public void run() {
      for (int i = 0; i < 50; i++) {
          phones.get();//调用消费产品方法
      }
   }  
}

/**
*产品的对象,生产的手机
*@author JEEP-711
*
*/
class Phones{
  @Override
  public String toString() {
      return "Phones [name=" + name + ", content=" + content + "]";
  }
  private String name;
  private String content;
  /**true表示可以生产,false表示可以消费
*作为标记,如何flag等于true表示可以生产,如何flag等于false表示不可生产
*如果flag等于false表示可以消费状态,可以取走,flag等于true表示不能取走
*解决重复值得问题
*/
  private boolean flag = true;//表示可以生产,false表示可以消费
  //构造方法
  public Phones(String name, String content) {
      super();
      this.name = name;
      this.content = content;
  }
  //取得名称方法
  public String getName() {
      return name;
  }
  //设置名称方法
  public void setName(String name) {
      this.name = name;
  }
  //取得内容方法
  public String getContent() {
      return content;
  }
  //设置内容方法
  public void setContent(String content) {
      this.content = content;
  }   

  /**
   *通过同步,解决了取值错误问题
   *@param name
   *@param content
   */
  //生产制造同步方法
  public synchronized void set(String name, String content){
      if(!flag){
          try {
          //调用该方法,当前线程进入等待池等待状态,没有指定时间,
          //需要其他线程唤醒,释放对象锁,让出cpu
              this.wait();
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
      }
      this.setName(name);
      try {
          Thread.sleep(300);
      } catch (InterruptedException e) {
          e.printStackTrace();
      }
      this.setContent(content);
      flag = false;//表示可以消费,取走
      this.notify();//唤醒在该监视器上的一个线程
  }   

  //消费产品同步取值方法
  public synchronized void get(){
      if(flag){
          try {
              //调用该方法,当前线程进入等待池等待状态,没有指定时间,
              //需要其他线程唤醒,释放对象锁,让出cpu
              this.wait();
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
      }
      try {
          Thread.sleep(300);
      } catch (InterruptedException e) {
          e.printStackTrace();
      }
      System.out.println(this.getName()+":"+this.getContent());
      flag = true;
      this.notify();
   }  
}

初学(面向对象近阶段) Ⅳ 难点: ★★★★★★★

希望每一篇文章都能够对读者们提供帮助与提升,这乃是每一位笔者的初衷

感谢您的阅读 欢迎您的留言与建议


  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值