荏苒追寻个人博客

做一个有追求的青年


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 日程表

Java 设计模式10——外观模式

发表于 2021-06-11 | 分类于 Java , 设计模式

模式定义

外观模式,结构型模式,隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。通过这个接口来向客户端隐藏系统内部的复杂性。简单来说,就是通过一个类来讲复杂系统的访问简化,使得客户端只用通过这个类来访问(通常是方法调用)系统,而无需关系系统该功能的具体实现。

代码示例

现在有一个绘图系统,可以绘制圆形、长方形、三角形,现在有一个只认识几个字的小白,要让他使用系统,我们就要创建一个小白页面,告诉他按那个可以画圆形,按哪个可以画长方形,这样这个小白在使用系统时,只需要简单的点击界面上的几个按钮就可以使用绘图系统,根本不需要掌握系统到底是则呢实现的。我的提供的这个小白页面就是系统功能的抽象外观。具体实现如下:

  1. 建立绘图系统,提供圆形、长方形、三角形的绘制方法;
  2. 建立一个小白界面,让小白用户直接使用;
  3. 小白通过小白界面调用系统。

首先建立绘图系统

1
2
3
public interface Shape {
void draw();
}
1
2
3
4
5
6
7
public class Circle implements Shape {

@Override
public void draw() {
System.out.println("Circle::draw()");
}
}
1
2
3
4
5
6
7
public class Rectangle implements Shape {

@Override
public void draw() {
System.out.println("Rectangle::draw()");
}
}
1
2
3
4
5
6
7
public class Triangle implements Shape {

@Override
public void draw() {
System.out.println("Triangle::draw()");
}
}

然后,建立小白页面(绘图系统的使用终端)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class ShapeMaker {
private Shape circle;
private Shape rectangle;
private Shape triangle;

public ShapeMaker() {
circle = new Circle();
rectangle = new Rectangle();
triangle = new Triangle();
}

void draw() {
drawCircle();
drawRectangle();
drawTriangle();
}

public void drawCircle(){
circle.draw();
}
public void drawRectangle(){
rectangle.draw();
}
public void drawTriangle(){
triangle.draw();
}
}

小白用户调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class FacadeClient {

public static void main(String[] args) {
ShapeMaker shapeMaker = new ShapeMaker();

shapeMaker.draw();

shapeMaker.drawCircle();
shapeMaker.drawRectangle();
shapeMaker.drawTriangle();
}
}
// 输出结果
Circle::draw()
Rectangle::draw()
Triangle::draw()
Circle::draw()
Rectangle::draw()
Triangle::draw()

模式总结

外观模式,其实就是让客户端与系统不耦合,让外观类(上例中的ShapeMaker)与系统耦合。

优缺点

  • 优点
    • 使用简单,用户只需要通过终端调用操作即可;
    • 客户端与具体的系统实现解耦了,具体系统只与小白界面(外观)耦合
    • 灵活,需要增加新的图形绘制,只需要系统增加新图形绘制能力,然后小白界面增加功能调用即可;
    • 安全,用户无法直接接触到系统。
  • 缺点
    • 违反了开闭原则,要扩展功能,就必须修改系统代码,必修修改小白界面的外观。

使用场景

单系统存在多个环节,多个功能,能独立调用时,为了规避使用的复杂性,可以使用外观模式,让使用者通过一个提供了系统功能访问能了的小白界面(外观)来使用系统,既安全又易用。

Java 设计模式08——组合模式

发表于 2021-06-10 | 分类于 Java , 设计模式

模式定义

合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。

这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。

从上面的定义可以提取到以下关键词:部分与整体、树形结构。

阅读全文 »

Java 设计模式07——桥接模式

发表于 2021-06-09 | 分类于 Java , 设计模式

引子

假设有一个绘制图形并上色的需求,要绘制的图形有三种:圆形、正方形、三角形,可以上的颜色有三种:红色、绿色和蓝色。也就是说最终可能有九种组合,这时有多种实现方式:

  1. 定义相应的图形,通过继承,分别实现红色圆形、绿色圆形、蓝色圆形等,这样子类就有9个;
  2. 按图形和颜色两个维度,定义好图形和颜色,然后按需要将他们进行组合;

上面的方式一实现很简单,也容易理解,但会导致类的数据过多,而且如果我需要新增一种图形或颜色,就需要重新写子类,导致类的结构过于臃肿,而方式二利用的就是桥接模式的思想,能很好的解决这种场景下的问题。

模式定义

桥接模式,结构性模式,即将抽象部分与实现部分分离,使他们可以独立变化。那上面的场景来说,我们要实现的部分是绘制带有颜色的形状,那么要实现的部分就可以抽象出形状和颜色两个类,然后将形状和颜色关联起来,这样就可以将形状和颜色自由组合了。

将抽象部分与他的实现部分分离套用《大话设计模式》里面的就是实现系统可能有多个角度分类,每一种角度都可能变化,那么把这种多角度分类给分离出来让他们独立变化,减少他们之间耦合。

桥接模式中的所谓脱耦,就是指在一个软件系统的抽象化和实现化之间使用关联关系(组合或者聚合关系)而不是继承关系,从而使两者可以相对独立地变化,这就是桥接模式的用意。

阅读全文 »

Java 设计模式06——适配器模式

发表于 2021-06-08 | 分类于 Java , 设计模式

模式定义

适配器模式,属于结构型模式,是将一个类的接口(源接口)转换成客户希望的另外一个接口(目标接口)。使得原本由于接口不兼容而不能一起工作的那些类可以一起工作,经过适配后,通过适配类可以使源接口目标接口一起工作。根据适配的写法不同,主要分为通过类型适配和通过对象适配。

类型适配

适配类继承源接口的实现类,并实现目标接口,通过类型继承来实现。

对象适配

适配类持有源接口实现类的对象引用,并通过构造函数传入,并实现目标接口。Java中ForkJoinTask中就大量的运用了这种类型适配方法,有了这些适配方法,像Runnable、Callable都可以在适配后作为ForkJoinTask提交到ForkJoinPool中。

模式示例

ForkJoinPool中接收的任务类型是ForkJoinTask,如果我们要将实现Runnable的对象和实现Callable接口的对象(二者都是可执行的)提交到ForkJoinPool中,我们就需要对Runnable和Callable进行适配。下面看具体代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* Adaptor for Runnables. This implements RunnableFuture
* to be compliant with AbstractExecutorService constraints
* when used in ForkJoinPool.
*/
static final class AdaptedRunnable<T> extends ForkJoinTask<T>
implements RunnableFuture<T> {
final Runnable runnable;
T result;
AdaptedRunnable(Runnable runnable, T result) {
if (runnable == null) throw new NullPointerException();
this.runnable = runnable;
this.result = result; // OK to set this even before completion
}
public final T getRawResult() { return result; }
public final void setRawResult(T v) { result = v; }
public final boolean exec() { runnable.run(); return true; }
public final void run() { invoke(); }
private static final long serialVersionUID = 5232453952276885070L;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* Adaptor for Runnables without results
*/
static final class AdaptedRunnableAction extends ForkJoinTask<Void>
implements RunnableFuture<Void> {
final Runnable runnable;
AdaptedRunnableAction(Runnable runnable) {
if (runnable == null) throw new NullPointerException();
this.runnable = runnable;
}
public final Void getRawResult() { return null; }
public final void setRawResult(Void v) { }
public final boolean exec() { runnable.run(); return true; }
public final void run() { invoke(); }
private static final long serialVersionUID = 5232453952276885070L;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* Adaptor for Runnables in which failure forces worker exception
*/
static final class RunnableExecuteAction extends ForkJoinTask<Void> {
final Runnable runnable;
RunnableExecuteAction(Runnable runnable) {
if (runnable == null) throw new NullPointerException();
this.runnable = runnable;
}
public final Void getRawResult() { return null; }
public final void setRawResult(Void v) { }
public final boolean exec() { runnable.run(); return true; }
void internalPropagateException(Throwable ex) {
rethrow(ex); // rethrow outside exec() catches.
}
private static final long serialVersionUID = 5232453952276885070L;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* Adaptor for Callables
*/
static final class AdaptedCallable<T> extends ForkJoinTask<T>
implements RunnableFuture<T> {
final Callable<? extends T> callable;
T result;
AdaptedCallable(Callable<? extends T> callable) {
if (callable == null) throw new NullPointerException();
this.callable = callable;
}
public final T getRawResult() { return result; }
public final void setRawResult(T v) { result = v; }
public final boolean exec() {
try {
result = callable.call();
return true;
} catch (Error err) {
throw err;
} catch (RuntimeException rex) {
throw rex;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public final void run() { invoke(); }
private static final long serialVersionUID = 2838392045355241008L;
}

上面采用的都是对象适配的方式,这也是更推荐的适配器模式实现方式,它采用的时组合方式,而设计模式的基本原则第6条推荐优先使用组合而不是继承。

再看看另一个例子。

定义两个接口:FlyAnimal(飞行动物)和CrawlAnimal(爬行动物);定义两个类:飞行动物实现类Bird以及爬行动物实现类Pig。

假设现在有一个飞行动物的聚会,而我们想让Pig这个爬行动物参加,这时我们就需要将Pig伪装(适配)成飞行动物。这样Pig就可以用伪装的身份去参加飞行动物的聚会了,具体代码如下:

1
2
3
4
5
public interface CrawlAnimal {
String crawlKindName();

void crawl();
}
1
2
3
4
5
public interface FlyAnimal {
String flyKindName();

void fly();
}
1
2
3
4
5
6
7
8
9
10
11
public class Pig implements CrawlAnimal{
@Override
public String crawlKindName() {
return "My kind is Pig";
}

@Override
public void crawl() {
System.out.println("I can crawl");
}
}
1
2
3
4
5
6
7
8
9
10
11
public class Bird implements FlyAnimal {
@Override
public String flyKindName() {
return "My kind is Bird";
}

@Override
public void fly() {
System.out.println("I can fly");
}
}

下面通过两种形式对Pig进行伪装,让其能参加飞行类动物的聚会。

类型适配

1
2
3
4
5
6
7
8
9
10
11
public class PigClassAdapter extends Pig implements FlyAnimal{
@Override
public String flyKindName() {
return super.crawlKindName();
}

@Override
public void fly() {
super.crawl();
}
}

对象适配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class PigObjectAdapter implements FlyAnimal{
private Pig pig;

public PigObjectAdapter(Pig pig) {
this.pig = pig;
}

@Override
public String flyKindName() {
return pig.crawlKindName();
}

@Override
public void fly() {
pig.crawl();
}
}

模式总结

适配器模式为只能相似的类或接口之间的兼容提供了一个兼容性方案,这种方案的代价就是在通过建立新的适配类以类型适配或对象适配的方式来让本来不能一起工作的类或接口能在一起工作。

优缺点

  • 优点
    • 1、可以让任何两个没有关联的类一起运行。
    • 2、提高了类的复用。
    • 3、增加了类的透明度。
    • 4、灵活性好。
  • 缺点
    • 过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。 (过于灵活的东西通常都会存在类似问题)
    • 2.由于 JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。

使用场景

不能因为适配器模式的灵活性而滥用,适配器模式一般是对现有系统的一种兼容,当我们目标明确的要让正在运行的某个接口能使用另一个接口的某些功能时,我们可以使用适配器模式。

适配器模式是为了解决正在运行的系统问题而提出来的,只是一种解决现有问题的一种兼容措施。在系统设计的时候应该尽量避免这种情况的出现,而不是将适配器模式考虑在内。

Java 设计模式05——原型模式

发表于 2021-06-07 | 分类于 Java , 设计模式

模式定义

原型模式属于对象的创建模式。通过给出一个原型对象来指明所有创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象。

模式结构

原型模式最主要的一点就是要求 原型对象 拥有克隆自己的能力,这样就可以调用其克隆方法来创建一个和原型对象类型相同的实例。原型模式主要有两种结构:简单形式和登记形式。

简单形式

简单形式中的三个角色:

  1. 客户角色,即提出创建对象请求的类;
  2. 抽象原型,即提供创建对象能力的角色,通常是接口或抽象类,通常可以使用Java提供的Cloneable接口,也可以自己实现;
  3. 具体原型,具体要创建出来的对象类型,实现(或覆盖)了抽象原型定义的接口方法。

具体代码示例:

抽象原型

1
2
3
public interface AbsPrototype {
Object clone();
}

具体原型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class RealPrototype implements AbsPrototype {
private String name = "real";

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Override
public Object clone() {
return new RealPrototype();
}
}

客户角色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class SimpleClient {
private final AbsPrototype prototype;

public SimpleClient(AbsPrototype prototype) {
this.prototype = prototype;
}

public AbsPrototype makePrototype() {
return (AbsPrototype) prototype.clone();
}

public static void main(String[] args) {
RealPrototype realPrototype = new RealPrototype();

SimpleClient client = new SimpleClient(realPrototype);
RealPrototype copy = (RealPrototype) client.makePrototype();
copy.setName("copyOne");
System.out.println(realPrototype.getName());
System.out.println(copy.getName());
}
}

登记形式

大体上和简单形式相同,只不过多了一个管理类,用键值对来将 抽象原型对象 不同的实现保存起来,然后要创建不同类型的 具体原型对象,只要根据相应的键来进行复制即可。

抽象原型对象的另一种实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class AnotherPrototype implements AbsPrototype {
private String name = "another";

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Override
public Object clone() {
return new AnotherPrototype();
}
}

不同类型原型对象管理角色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class PrototypeManager {

private static volatile PrototypeManager instance;

public static PrototypeManager getInstance() {
if (instance == null) {
synchronized (PrototypeManager.class) {
if (instance == null) {
instance = new PrototypeManager();
}
}
}
return instance;
}

private PrototypeManager() {
this.prototypeMap = new HashMap<>();
}

private final Map<String, AbsPrototype> prototypeMap;

public void setPrototype(String prototypeId, AbsPrototype prototype) {
prototypeMap.put(prototypeId, prototype);
}

public void removePrototype(String prototypeId) {
prototypeMap.remove(prototypeId);
}

public AbsPrototype getPrototype(String prototypeId) {
return prototypeMap.get(prototypeId);
}
}

客户端角色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class CheckoutClient {
public static void main(String[] args) {
AbsPrototype p1 = new RealPrototype();
PrototypeManager.getInstance().setPrototype("p1", p1);

AbsPrototype p2 = new AnotherPrototype();
PrototypeManager.getInstance().setPrototype("p2", p2);

// 得到 RealPrototype的对象
RealPrototype copyReal = (RealPrototype) PrototypeManager.getInstance().getPrototype("p1");
AnotherPrototype copyAnother = (AnotherPrototype) PrototypeManager.getInstance().getPrototype("p2");

/*
* 由此例可以看出原型这种几种管理的几个明显缺点:
* 1. 容易产生 NullPointerException,得到的基础原型对象可能会被移除而不复存在了
* 2. 容易产生 ClassCastException,得到的对象可能是抽象原型的另一种实现
*
* 所以要保证只有一种类型的具体对象,这样就可以避免可能发生的类型转换问题。
*/
}
}

改进版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class PrototypeManager {
public static final String PROTOTYPE_REAL = "real";
public static final String PROTOTYPE_ANOTHER = "another";
private static volatile PrototypeManager instance;

public static PrototypeManager getInstance() {
if (instance == null) {
synchronized (PrototypeManager.class) {
if (instance == null) {
instance = new PrototypeManager();
}
}
}
return instance;
}

private PrototypeManager() {
this.prototypeMap = new HashMap<>();
}

private final Map<String, AbsPrototype> prototypeMap;

public void removePrototype(String prototypeId) {
prototypeMap.remove(prototypeId);
}

public AbsPrototype getPrototype(String prototypeId) {
AbsPrototype absPrototype = prototypeMap.get(prototypeId);
if (absPrototype == null) {
if (PROTOTYPE_REAL.equals(prototypeId)) {
absPrototype = new RealPrototype();
prototypeMap.put(PROTOTYPE_REAL, absPrototype);
} else if (PROTOTYPE_ANOTHER.equals(prototypeId)){
absPrototype = new AnotherPrototype();
prototypeMap.put(PROTOTYPE_ANOTHER, absPrototype);
} else {
throw new IllegalArgumentException("prototypeId:" + prototypeId + " not supported, please check it out!");
}
}
return absPrototype;
}
}

相应的CheckoutClient改为:

1
2
3
4
5
6
7
8
9
public class CheckoutClient {

public static void main(String[] args) {

// 得到 RealPrototype的对象
RealPrototype copyReal = (RealPrototype) PrototypeManager.getInstance().getPrototype(PrototypeManager.PROTOTYPE_REAL);
AnotherPrototype copyAnother = (AnotherPrototype) PrototypeManager.getInstance().getPrototype(PrototypeManager.PROTOTYPE_ANOTHER);
}
}

原型对象的创建都集中在PrototypeManager中,新增支持的原型对象只需要定义新增的原型对象,然后相应的添加到PrototypeManager中即可。

模式总结

  1. 应用场景,当需要大范围的或者平凡的创建某类对象时;
  2. 应用先决条件,原型对象必须支持克隆自己的方法;

延伸拓展

上面说了,原型模式实现的前提是原型对象必须支持克隆自己的方法。这里的克隆有两种形式:浅克隆和深克隆。

浅克隆与深克隆

浅克隆:只负责克隆按值传递的数据(比如基本数据类型、String类型),而不复制它所引用的对象,换言之,所有的对其他对象的引用都仍然指向原来的对象。

深克隆:除了浅度克隆要克隆的值外,还负责克隆引用类型的数据。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深度克隆把要复制的对象所引用的对象都复制了一遍,而这种对被引用到的对象的复制叫做间接复制。

深度克隆要深入到多少层,是一个不易确定的问题。在决定以深度克隆的方式复制一个对象的时候,必须决定对间接复制的对象时采取浅度克隆还是继续采用深度克隆。因此,在采取深度克隆时,需要决定多深才算深。此外,在深度克隆的过程中,很可能会出现循环引用的问题,必须小心处理。

一般的Object的clone()就是浅克隆,而Java中通常通过序列化来实现对象的深克隆,即先将对象序列化到流中,然后再从反序列化读出对象。对于不能被序列化的对象,需要用transient将其排除在克隆之外。

Java 设计模式03——抽象工厂模式

发表于 2021-06-06 | 分类于 Java , 设计模式

模式定义

抽象工厂模式,提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类;理解起来就是抽象工厂的接口定义中可以有多个接口方法,这些接口方法分别用来创建不同的对象。

模式示例

以台式电脑组装为例,不同的电脑厂商会选用不同的电脑配件,这里的主要配件有CPU、显示器、主板等,不同的品牌采用的不同的配件组装成电脑售卖,当然我们也可以自己组装电脑。这里就简单列出几个品牌,如惠普、苹果以及自己组装。这三个品牌对应的就是三个具体的工厂。

  1. 定义好配件,如CPU、显示器、主板等;
  2. 定义好抽象工厂,即创建CPU、创建显示器和创建主板;
  3. 定义好具体工厂,即华为电脑工厂、苹果电脑工厂;
  4. 示例检验客户端定义。

具体代码

配件的定义

CPU基类

Cpu.java

1
2
3
public abstract class Cpu {
public abstract String cpuName();
}

HuaweiCpu.java

1
2
3
4
5
6
public class HuaweiCpu extends Cpu {
@Override
public String cpuName() {
return "华为自研芯片";
}
}

XiaoLongCpu.java

1
2
3
4
5
6
public class XiaoLongCpu extends Cpu {
@Override
public String cpuName() {
return "骁龙855芯片";
}
}

显示器基类

Monitor.java

1
2
3
public abstract class Monitor {
public abstract String monitorName();
}

RetinaMonitor.java

1
2
3
4
5
6
7
public class RetinaMonitor extends Monitor {

@Override
public String monitorName() {
return "Retina显示器";
}
}

PhilipsMonitor.java

1
2
3
4
5
6
7
public class PhilipsMonitor extends Monitor {

@Override
public String monitorName() {
return "飞利浦显示器";
}
}

主板基类

MainBoard.java

1
2
3
public abstract class MainBoard {
public abstract String mainBoardName();
}

AsusMainBoard.java

1
2
3
4
5
6
public class AsusMainBoard extends MainBoard {
@Override
public String mainBoardName() {
return "华硕主板";
}
}

GigaMainBoard.java

1
2
3
4
5
6
public class GigaByteMainBoard extends MainBoard {
@Override
public String mainBoardName() {
return "技嘉主板";
}
}

定义抽象工厂

ComputerFactory.java,代表抽象的电脑生成线。

1
2
3
4
5
6
7
8
9
10
11
12
public abstract class ComputerFactory {
public abstract Cpu createCpu();

public abstract MainBoard createMainBoard();

public abstract Monitor createMonitor();

public void computerInfo() {
System.out.println("生产的电脑型号描述:CPU->" + createCpu().cpuName() +
", 主板->" + createMainBoard().mainBoardName() + ", 显示器->" + createMonitor().monitorName());
}
}

定义具体的工厂

AppleComputerFactory.java,代表苹果电脑生产线。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class AppleComputerFactory extends ComputerFactory {

@Override
public Cpu createCpu() {
return new A14Cpu();
}

@Override
public MainBoard createMainBoard() {
return new GigaByteMainBoard();
}

@Override
public Monitor createMonitor() {
return new RetinaMonitor();
}
}

HuaweiComputerFactory.java,代表华为电脑生产线。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class HuaweiComputerFactory extends ComputerFactory {

@Override
public Cpu createCpu() {
return new HuaweiCpu();
}

@Override
public MainBoard createMainBoard() {
return new AsusMainBoard();
}

@Override
public Monitor createMonitor() {
return new PhilipsMonitor();
}
}

定义示例客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class AbsFactoryClient {
public static void main(String[] args) {
ComputerFactory computerFactory = new AppleComputerFactory();

computerFactory.computerInfo();

ComputerFactory computerFactory1 = new HuaweiComputerFactory();

computerFactory1.computerInfo();

}
}
// 输出结果
生产的电脑型号描述:CPU->苹果A14放生芯片, 主板->技嘉主板, 显示器->Retina显示器
生产的电脑型号描述:CPU->华为自研芯片, 主板->华硕主板, 显示器->飞利浦显示器

模式总结

通过比较抽象工厂模式和工厂方法模式,你会发现抽象工厂模式中工厂的实现类中提供了创建不同对象的方法,这些不同的对象间存在着一种外在的联系,如本例中同一个工厂中穿件的对象属于一条电脑生产线上的。如果我们要为电脑生产线增加键盘这种流水化操作,那么我们同样需要修改所有的工厂类,但是如果我们要开辟一条新的生产线,我们就不用动原来的代码,而是直接将这个生产线(具体工厂类)定义出来即可。可见抽象工厂模式有如下特点:

  1. 改变生产线同样会违背“开闭原则”,新增生产线可以不需要修改代码,符合“开闭原则”;
  2. 不用关心对象的创建过程,改变生产线中某个配件就十分方便。

通用功能——UML 类图箭头和线条详解

发表于 2021-06-01 | 分类于 通用

前言

UML,Unified Modeing Language,统一建模语言,是个很庞大的概念,本篇想要讲的是UML中比较普遍的箭头即线条代表的具体含义,以便日后遇到UML图能了解不同类及对象之间的关系。

阅读全文 »

Java 设计模式12——代理模式

发表于 2021-05-31 | 分类于 Java , 设计模式

https://rainmonth.github.io/posts/J210531.html

模式定义

给某一个对象(源对象)提供一个代理对象,对这个对象(源对象)的访问都通过这个代理对象来进行。这样的话对源对象的访问都可以通过代理对象来进行,代理对象可以在不改变源对象的基础上,控制对这个源对象的访问。这类似于明星(源对象)和明星经纪人(代理对象)的关系。某公司想请某明星A做形象代言人,于是就找到了明星A的经纪人B,B若代言费用合理,就允许其代言,不合理就不为其代言。隔离了外界对明星A的干扰。

代理实现方式

代理模式主要分三种:静态代理、动态代理和Cglib代理。

  • 静态代理,为代理的类在额外写一个类(或者用工具生成一个类),这个类实现了被代理的类的所有功能。这就导致了要为一个对象设置代理对象,必须就在对象类的基础上再写一个类,显得不够灵活;
  • 动态代理,动态代理实际上是程序在运行中,根据被代理的接口来动态生成代理类的class文件,并加载class文件运行的过程。由于动态生成的类已经继承了Proxy这个类,就不能在继承其他类了,从而只能通过接口的方式来实现了。(Java是单继承,多实现的)
  • Cglib代理,通过为被代理的类生成一个子类并重写被代理的方法实现代码的动态植入,因此,Cglib无法代理被final修饰的类或方法以及静态方法。
阅读全文 »

Android 进程保活——守护进程

发表于 2021-02-20 | 分类于 Android , 进程保活

本文链接:https://rainmonth.github.io/posts/A2102200.html

Android守护进程

背景

最近项目有个需求,需要在App进程被干掉时,向服务器提交一些数据,一脸懵逼,寻思着看Android系统是否提供了进程被干掉的监听,发现没有(iOS同学表示他们时有的),如是就搜集了些资料,达到一些曲线救国的实现方式。

要实现进程保活,就需要进程的分类及优先级(什么样的进程容易被系统杀死,什么样的不容易被进程杀死),以下按优先级从高到低的顺序(越高越不容易杀死)排列:

参考文章

  • Android进程保活之一个像素保活
  • android保活进程总结—双进程保活策略
  • Android后台运行白名单,优雅实现进程保活

小白学理财——基金基础知识

发表于 2021-02-20 | 分类于 理财 , 基金

https://rainmonth.github.io/posts/F202102200.html

<i class="fa fa-angle-left" aria-label="上一页"></i>1…101112…22<i class="fa fa-angle-right" aria-label="下一页"></i>

216 日志
43 分类
43 标签
GitHub
© 2025 Randy Zhang
由 Hexo 强力驱动
|
主题 — NexT.Gemini v6.1.0