云计算百科
云计算领域专业知识百科平台

Java设计模式(一)

目录

单例模式

工厂模式

抽象工厂

1.定义产品接口

2.具体产品实现

3.定义抽象工厂接口

4.具体工厂实现

5.客户端代码

静态工厂

模版模式

1.抽象类定义算法骨架

2.具体子类实现可变部分

3.客户端使用方式

策略模式

基于面向对象的策略模式

基于面向函数接口的策略模式

代理模式

  静态代理


单例模式

        描述:一个类永远只产生这个类的唯一对象。

        构造函数私有,限制在外部类中去创建对象,若在外部能够创建对象,会导致各个对象在堆内存的存储的数据不一致。

  • 恶汉式单例 :在类加载时就创建对象,占用堆内存。
  • 懒汉式单例:需要这个对象的时候,才去创建,不提前占用堆内存。

//一个类永远只产生这个类的唯一对象
public class ParseProperties {

//恶汉式单例:没有调用getInstance(),这个对象就存在。占用堆内存。
// private static ParseProperties p = new ParseProperties();

//懒汉式单例:需要这个对象的时候,才去创建,懒的体现是不提前占用堆内存
private static ParseProperties p ;

//构造函数私有,限制了外部类去new对象,创建对象
private ParseProperties()
{

}

//通过类名.static方法调用
public static ParseProperties getInstance()
{
//懒汉式
if(null == p)
{
p = new ParseProperties();
}

return p;
}

}

下面的代码体现了懒汉式单例模式

(1)构造函数私有:防止外部类直接通过new创建实例,确保只能通过类提供的静态方法获取实例。

(2)静态私有实例变量:这个静态变量用于保存类的唯一实例。

(3)静态公共获取方法:这是获取类实例的唯一入口,控制着实例的创建和访问。

package demo1;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.util.Properties;

public class ParseProFile {

private static ParseProFile p; //懒汉式单例

// 构造函数私有,限制在外部类中去创建对象,若在外部类中能够创建对象,会导致各个对象在堆内存的存储的数据不一致
private ParseProFile()
{
Properties p = new Properties();
try {

Reader reader = new FileReader("./src/main/java/demo1/demo.properties");
p.load(reader);

String str = p.getProperty("port");
System.out.println(str);

String queryStuSql = p.getProperty("queryStuSQL");
System.out.println(queryStuSql);

} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

}

public static ParseProFile getInstance()
{

if(null == p)
{
p = new ParseProFile(); //只有在第一次调用getInstance()时才创建实例,而不是在类加载时就创建。
}

return p; //p是静态变量,存在于静态全局数据区,当new过一次后p存在,后面直接复用就行,所以地址都相同。

}
}
属性文件:"./src/main/java/demo1/demo.properties"
port=8090
queryStuSQL=select * from t_stus
package demo1;

public class Test {

public static void main(String[] args) {

ParseProFile p1 = ParseProFile.getInstance();
ParseProFile p2 = ParseProFile.getInstance();
ParseProFile p3 = ParseProFile.getInstance();

//多次调用getInstance()返回的都是同一个对象。
System.out.println(p1==p2);
System.out.println(p2==p3);
}
// 后续还需考虑线程安全问题
}

运行结果如下:

下面是通过鸿蒙(HarmonyOS)ArkUI 的布局示例,体现类似恶汉式和懒汉式的单例模式,它们可以通过 数据加载和渲染时机 来类比这两种设计模式的思想。

(1)体现恶汉式单例模式:(Scroll + ForEach布局)

  • 一次性加载所有数据(100 项全部初始化)。

  • 即使某些 Row 不在可视区域,也会被 ForEach 遍历并创建。

  • 类似于恶汉式:提前加载所有数据,占用内存较多,但滚动时不会动态加载。

@Entry
@Component
struct ScrollPage {

arrs:string[] = []

aboutToAppear(): void {
for(let i =0;i<100;i++)
{
this.arrs.push("第" + i + "项");
}
}

loadItem(item:string,index:number):string{
console.log("加载的项是:" +index)
return item
}

build() {

Scroll()
{
Column()
{
ForEach(this.arrs,(item:string,index:number)=>{
Row()
{
Text(this.loadItem(item,index)).fontSize(30)
}.width("100%").height("10%").backgroundColor("#aabbcc")
.margin({top:10}).borderRadius(20).justifyContent(FlexAlign.Center)
})
}

}.width("100%").height("100%")

}
}

运行效果如下:

(2)体现懒汉式单例模式:(List + ForEach布局)

  • 按需渲染:List 组件自带懒加载优化。

  • 只有滚动到某个 ListItem 时,才会真正创建并渲染它。

  • 类似于懒汉式:仅加载当前可见的项,节省内存,但滚动时可能需要动态加载。

@Entry
@Component
struct ListPage {
arrs: string[] = []

aboutToAppear(): void {
for (let i = 0; i < 100; i++) {
this.arrs.push("第" + i + "项");
}
}

loadItem(item: string, index: number): string {
console.log("加载的项是:" + index)
return item
}

build() {
List() {
ForEach(this.arrs, (item: string, index: number) => {
ListItem() {
Text(this.loadItem(item, index)).fontSize(30)
}
.width("100%")
.height("10%")
.backgroundColor("#aabbcc")
.margin({ top: 10 })
.borderRadius(20)
})
}.width("100%").height("100%")
}

}

运行效果如下:

总结:

特性恶汉式懒汉式
初始化时机 提前加载(如 aboutToAppear 时就初始化) 按需加载(如滚动到某个位置才加载)
内存占用 可能占用较多内存(一次性加载所有数据) 节省内存(仅加载部分)
性能影响 首次加载可能较慢 滚动时可能卡顿(动态加载)

工厂模式

        描述:根据用户的需求工厂类产生实际的对象。主要是类的设计,动态绑定,方法重写。

抽象工厂

        一个工厂类创建一个产品族(多个相关产品)。

  • 抽象与实现分离:客户端代码只依赖抽象接口,不依赖具体实现
  • 易于扩展:要添加新的产品类型,只需要添加新的工厂类和产品类,不需要修改现有代码
  • 动态绑定:通过运行时决定使用哪个具体工厂
  • 单一职责:每个工厂只负责创建自己产品族的产品

下面通过一个动物农场模拟器来体现抽象工厂模式的核心思想和结构:

1.定义产品接口

(1)动物接口

//动物接口
public interface Animal {
void makeSound();
void eat();
}

(2)食物接口

// 食物接口
public interface Food {
void showNutrition();
}

2.具体产品实现

(1)猫产品族

public class Cat implements Animal {
@Override
public void makeSound() {
System.out.println("喵喵喵~");
}

@Override
public void eat() {
System.out.println("猫在吃鱼");
}
}
public class Fish implements Food {
@Override
public void showNutrition() {
System.out.println("鱼类食物:富含蛋白质和Omega-3");
}
}

(2)狗产品族

public class Dog implements Animal {
@Override
public void makeSound() {
System.out.println("汪汪汪!");
}

@Override
public void eat() {
System.out.println("狗在啃骨头");
}
}
public class Bone implements Food {
@Override
public void showNutrition() {
System.out.println("骨头:富含钙质,有助于牙齿健康");
}
}

体现点:

  • 产品分为抽象接口和具体实现

  • 产品按类型形成等级结构(动物、食物)

  • 具体产品属于特定产品族(猫族、狗族)

3.定义抽象工厂接口

public interface AnimalFarmFactory {
Animal createAnimal();
Food createFood();
}

体现点:

  • 定义了创建"产品族"的接口(动物和食物)

  • 不关心具体实现,只声明能创建哪些类型的产品

  • 符合"抽象工厂"中"抽象"的概念

4.具体工厂实现

(1)猫工厂

public class CatFarmFactory implements AnimalFarmFactory {
@Override
public Animal createAnimal() {
return new Cat();
}

@Override
public Food createFood() {
return new Fish();
}
}

(2)狗工厂

public class DogFarmFactory implements AnimalFarmFactory {
@Override
public Animal createAnimal() {
return new Dog();
}

@Override
public Food createFood() {
return new Bone();
}
}

体现点:

  • 每个具体工厂负责创建一个完整的产品族(猫+鱼,狗+骨头)

  • 符合"一个工厂创建一系列相关对象"的原则

5.客户端代码

package abstratcDemo2;

import java.util.Scanner;

public class FarmSimulator {
public static void main(String[] args) {
System.out.println("欢迎来到动物农场模拟器!");
System.out.println("请选择要创建的农场类型:1. 猫农场 2. 狗农场");

Scanner scanner = new Scanner(System.in);
int choice = scanner.nextInt();

AnimalFarmFactory factory = null;

if(choice == 1){
factory = new CatFarmFactory(); //动态绑定
}
else if(choice == 2){
factory = new DogFarmFactory(); //动态绑定
}

// 使用工厂创建产品(方法重写)
Animal animal = factory.createAnimal();
Food food = factory.createFood();

System.out.println("农场创建成功:");
System.out.print("动物行为:");
animal.makeSound();

System.out.print("进食行为:");
animal.eat();

System.out.print("食物营养:");
food.showNutrition();

}
}

体现点:

  • 客户端只依赖抽象接口,不依赖具体实现

  • 可以轻松切换整个产品族(只需切换动态绑定的工厂实例)

实现效果:

静态工厂

        工厂类里面写静态方法,通过类名调用。

        所有对象的创建逻辑集中在一个静态方法中,不需要创建多个工厂子类。

静态工厂特点:

  • 不需要实例化工厂类
  • 通过类名直接调用静态方法
  • 通常用于创建逻辑简单、不需要维护状态的场景

下面的代码展示了静态工厂模式的实现方式,工厂类AnimalFactory包含一个静态方法createAnimal,客户端通过类名直接调用静态方法创建对象:AnimalFactory.createAnimal(),将对象创建的细节封装在工厂类中。

public interface Animal {
public void makeAnimal();
}
public class AnimalFactory {

// 工厂类里面写静态方法,通过类名调用
public static Animal createAnimal(String name)
{
Animal an = null;

if(name.equals("狗"))
{
an = new Dog();
}
if(name.equals("猫"))
{
an = new Cat();
}

//当产品类型很多时,静态工厂需要大量if-else分支
//面向对象解决不了的问题,后续通过面向反射才可以解决
return an;
}
}

public class Cat implements Animal {
@Override
public void makeAnimal() {
System.out.println("生产猫");
}
}
public class Dog implements Animal {
@Override
public void makeAnimal() {

System.out.println("生产狗");
}
}
package staticDemo;

import java.util.Scanner;

public class Test {

public static void main(String[] args) {
System.out.println("请问您需要制造什么动物?");

Scanner s = new Scanner(System.in);
String inputValue = s.next();

Animal an = AnimalFactory.createAnimal(inputValue);
an.makeAnimal();
}
}

运行效果如下:

模版模式

        描述:通过模板方法按照规定好的流程执行。

  • 固定流程控制:抽象类中的模版方法定义了不可变的算法骨架,使用final修饰防止子类修改流程。
  • 可变部分抽象化:将可能变化的部分声明为抽象方法,由子类实现具体行为。
  • 代码复用:公共行为在抽象类中实现,避免子类重复代码。

下面通过代码体现模版模式的结构和特点:

1.抽象类定义算法骨架

        在抽象类中定义一个模板方法(执行流程不希望被改变,用final关键字修饰,该方法不被子类继承)。

public abstract class AbstractHome {

//定义一个模板方法,用final关键字修饰,该方法不被子类继承
public final void homeFrameWork()
{
this.work();
this.goHomeType();
this.goHome();
}

public void work()
{
System.out.println("工作到点下班");
}

//回家的交通工具不一样,抽象方法,由子类实现
public abstract void goHomeType();

public void goHome()
{
System.out.println("到家了");
}
}

2.具体子类实现可变部分

public class HuaweiCar extends AbstractHome {
@Override
public void goHomeType() {
System.out.println("乘坐华为汽车回家");
}
}
public class XiaomiCar extends AbstractHome {
@Override
public void goHomeType() {
System.out.println("乘坐小米汽车回家");
}
}

3.客户端使用方式

public class Test {

public static void main(String[] args) {
AbstractHome ah = new HuaweiCar();
// AbstractHome ah = new XiaomiCar();

ah.homeFrameWork();
}

}

运行效果如下:

策略模式

        描述:策略类来绑定(通过构造函数或set方法)不同的具体的策略的实现类。

基于面向对象的策略模式

        策略类中的构造注入或set注入传入的参数是对象类型。

  • 消除条件语句:避免了在客户端使用大量的if-else或switch-case来选择算法
  • 算法复用:同一个策略可以被多个上下文使用
  • 灵活扩展:添加新策略不影响现有系统
  • 运行时动态切换:如示例中通过setLg()方法切换策略

下面的代码体现了基于面向对象的策略模式的实现:

1.策略接口 (LikeGril)

public interface LikeGril {
public void likeCategory(String type);
}

  • 定义了所有具体策略类必须实现的方法

  • 抽象了"追求方式"这一行为

2.具体策略类 (LikeType1/2/3)

public class LikeType1 implements LikeGril {
@Override
public void likeCategory(String type) {

System.out.println("使用方式1:"+ type+ "来获得芳心");
}
}
public class LikeType2 implements LikeGril {
@Override
public void likeCategory(String type) {

System.out.println("使用方式2:"+ type+ "来获得芳心");
}
}
public class LikeType3 implements LikeGril {
@Override
public void likeCategory(String type) {

System.out.println("使用方式3:"+ type+ "来获得芳心");
}
}

  • 每个类实现了不同的具体策略

  • 封装了具体的算法/行为实现

3.策略类 (LikeContext)

public class LikeContext {

//声明的接口对象,父类对象,可以绑定LikeType
private LikeGril lg;

//构造注入【spring】,通过LikeContext类的构造函数给接口LikeGril绑定具体的对象,完成对象的实例化
public LikeContext(LikeGril lg)
{
this.lg = lg;
}

//set注入【spring】,通过set方法传入对象
public void setLg(LikeGril lg)
{
this.lg = lg;
}

//执行喜欢的方法
public void execCategory(String type)
{
this.lg.likeCategory(type);
}

}

  • 维护对策略对象的引用

  • 提供切换策略的方法

  • 将客户端请求转发给当前策略

4.客户端使用方式

package strategyDemo1;

public class Test {

public static void main(String[] args) {

// 创建策略类并设置初始策略
LikeContext lc1 = new LikeContext(new LikeType2());
lc1.execCategory("香水");

// 运行时切换策略
lc1.setLg(new LikeType3());
lc1.execCategory("美食");

// 使用不同策略的另一个策略类对象
LikeContext lc2 = new LikeContext(new LikeType1());
lc2.execCategory("鲜花");
}
}

  • 客户端决定使用哪种策略

  • 可以在运行时切换策略

  • 多个策略类对象可以同时使用不同策略

运行效果如下:

基于面向函数接口的策略模式

        策略类中的构造注入或set注入传入的参数是函数(lambda表达式)。

  • 代码简洁性:省去了多个具体策略类的定义
  • 即时定义策略:可以在调用时现场定义策略行为
  • 函数式编程特性:可以方便地组合、传递策略函数

下面的代码体现了基于面向函数接口的策略模式的实现:

1.函数式策略接口

        使用@FunctionalInterface注解明确标记为函数式接口,里面只能有一个抽象方法。

@FunctionalInterface
public interface LikeGril {
public void likeCategory(String type);
}

2.策略类

package strategyDemo2;

public class User {

//声明的接口对象
private LikeGril lg;

// 构造注入
public User(LikeGril lg)
{
this.lg = lg;
}

// 执行策略
public void execCategory(String type)
{
this.lg.likeCategory(type);
}

}

3.客户端使用Lambda表达式

        直接使用Lambda表达式作为具体策略实现,每个Lambda相当于一个策略实现类,代码更加简洁直观。

public static void main(String[] args) {

User u1 = new User((type)->{ System.out.println("喜欢的方式有1:"+type+",来获取芳心"); });
u1.execCategory("鲜花");

User u2 = new User((type)->{ System.out.println("喜欢的方式有2:"+type+",来获取芳心"); });
u2.execCategory("美食");
}

运行效果如下:

代理模式

        代理模式是切面编程的核心模式。切面: 执行这个方法之前和之后做的业务逻辑。

        描述:代理类代理真实的业务类形成切面类对真实的业务类的拦截。

        前置拦截:鉴权,反爬虫机制, 设备的类型。

        后置拦截:一个方法的性能的数据采集。

  静态代理

优点:

  • 职责清晰:真实对象只需关注核心业务

  • 扩展性强:通过代理可以方便地添加额外功能

  • 保护真实对象:客户端不直接访问真实对象

缺点:

  • 每个真实类都需要一个代理类

  • 接口变化时,代理类和真实类都需要修改

下面通过代码来体现静态代理模式的实现:

1.定义接口

public interface Operator {
public void operatorHouse();
}

代理类Proxy和真实类StuOperator实现相同接口。

2.真实类:

//学生类 真实类
public class StuOperator implements Operator {
@Override
public void operatorHouse() {
System.out.println("学生想租有政府补贴的公租房");
}
}

3.代理类:

//代理类,中介,代理学生去租房子
public class Proxy implements Operator{

private Operator op; //代理持有真实对象的引用

private Interceptor in;

// 构造注入,真实类传给op,拦截类传给in
public Proxy(Operator op,Interceptor in)
{
this.op = op;
this.in = in;
}

@Override
public void operatorHouse() {

//代理控制对真实对象的访问
//前置拦截
this.in.beforeAction();
//租房子
this.op.operatorHouse(); //op传入的对象类型的参数是new StuOperator(),所以相当于StuOperator.operatorHouse()
//后置拦截
this.in.afterAction();

}
}

4.拦截的接口与具体的实现类:

//拦截的接口
public interface Interceptor {
public void beforeAction();
public void afterAction();
}

public class HouseInterceptor implements Interceptor {
@Override
public void beforeAction() {
System.out.println("检查您的身份证和政府的文件");
}

@Override
public void afterAction() {
System.out.println("检查您租房子的设备和其它装修的情况");

}
}

5.客户端代码:

package proxyDemo1;

public class Test {

public static void main(String[] args) {

//客户端创建代理对象,传入真实对象和拦截器
Proxy proxy = new Proxy(new StuOperator(),new HouseInterceptor()); //传入对象类型的参数(真实类,拦截类)

//客户端通过代理间接访问真实对象
proxy.operatorHouse();

}
}

 6.静态代理的执行流程

(1)客户端创建代理对象,传入真实对象和拦截器

Proxy proxy = new Proxy(new StuOperator(),new HouseInterceptor());

(2)客户端调用代理方法

proxy.operatorHouse();

(3)代理执行流程:

//通过动态绑定
1. 调用拦截器beforeAction()
2. 调用真实对象的operatorHouse()
3. 调用拦截器afterAction()

运行效果如下:

赞(0)
未经允许不得转载:网硕互联帮助中心 » Java设计模式(一)
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!