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

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

目录导航

[TOC]

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();
}
}

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

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

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

以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号