用好这4个设计模式,完成Java中的大部分任务
学好Java设计模式,提高代码质量、提高开发效率、提高代码可读性和可理解性。
为什么使用设计模式
Java中的设计模式提供了一种通用的解决方案,可以帮助开发人员解决常见的软件设计问题。设计模式是一种被广泛接受的最佳实践,它已经被证明可以提高代码的可读性、可维护性和可扩展性。
在Java中,设计模式被广泛应用于软件开发,特别是在大型的、复杂的软件系统中。设计模式提供了一种在面对复杂问题时的组织代码的方法,使得代码更易于理解和维护。
使用设计模式还可以避免一些常见的错误,例如过度耦合、低内聚性和不必要的复杂性。设计模式可以使代码更加灵活,易于扩展和修改,同时还可以提高代码的重用性和可测试性。
Java中的设计模式可以帮助开发人员更好地组织和管理代码,提高代码的质量和可维护性,从而使软件开发更加高效和可靠。
1、单例模式
在这种模式中,你只能创建一个类的实例。即使你创建多个引用变量,它们都将指向同一个对象。
相当简单明了,对吧?但是如何实现这个目标呢?如何确保只创建一个对象?通过一个例子,你将会更好地理解。
让我们来看一个名为Probe的类,它有其实例变量和方法。
class Probe {
// 实例变量
// 重要方法
}
我们的Probe
类不希望有多个对象存在,因此我们将其构造函数设为私有。
private Probe() {
// Initialize variables here
}
现在,这个构造函数只能从类内部调用。创建一个静态方法getInstance()
。
这个方法接受Probe
类的引用变量,并检查对象是否已经被实例化。如果没有,则创建一个新对象并返回给客户端。
private static Probe getInstance(Probe probe) {
if(probe == null)
probe = new Probe();
return probe;
}
由于getInstance()
是一个静态方法,它只能在类级别而不是对象级别调用。每次调用该方法时,它都返回相同的对象。因此,该模式能够阻止你创建多个对象。
单例模式用于只需要一个实例的对象。例如,用于注册表设置和日志记录的对象只需要一个实例,否则它们可能会导致意外的副作用。
2、观察者模式
假设你关注一个社交媒体页面。每当这个页面添加新帖子时,你都希望收到通知。
因此,当一个对象(页面)执行一个动作(添加一个帖子)时,另一个对象(关注者)会被通知。这种情况可以通过观察者模式来实现。
让我们创建一个Page
类和一个Follower
接口。页面可以有不同类型的关注者,如普通用户、招聘人员和官方人员。我们将为每个关注者类型创建一个类,并且所有这些类都将实现Follower
接口。
在这里,Page
类是主体,关注者是观察者类。观察者模式定义了主体和观察者之间的一对多关系。如果主体更改其状态(页面添加了一个新的帖子),则所有的观察者即关注者都会被通知。
Page
类将具有以下方法:
registerFollower()``:此方法注册新的关注者。
notifyFollowers():此方法通知所有关注者页面有新帖子。 `getLatestPost()`和`addNewPost()
:页面上最新帖子的getter
和setter
方法。
另一方面,Follower
接口只有一个方法`update()``,该方法将被实现此接口的关注者类型重写,也称为具体观察者。
interface Follower {
public void update();
}
当主体需要通知观察者状态更改即有新帖子时,将调用update()
方法。
让我们实现Page
类即主体。
class Page {
private ArrayList<Follower> followers;
String latestPost;
public Page() {
followers = new ArrayList();
}
public void registerFollower(Follower f) {
followers.add(f);
}
public void notifyFollowers() {
for(int i = 0; i < followers.size(); i++) {
Follower follower = followers.get(i);
follower.update();
}
}
public String getLatestPost() {
return latestPost;
}
public void addNewPost(String post) {
this.latestPost = post;
notifyFollowers();
}
}
在这个类中,我们有一个所有关注者的列表。当一个新的关注者想要关注页面时,它调用registerFollower()
方法。latestPost
保存页面添加的新帖子。
当添加新帖子时,将调用notifyFollowers()
方法,它会遍历每个关注者并通过调用update()
方法通知他们。
现在,让我们实现我们的第一种关注者——用户。
class User implements Follower {
Page page;
public User(Page page) {
this.page = page;
page.registerFollower(this);
}
public void update() {
System.out.println("Latest post seen by a normal user: " + page.getLatestPost());
}
}
当创建一个新的User
对象时,它会选择要关注的页面并注册。当页面添加新帖子时,User
通过它自己实现的update()
方法得到通知。
让我们再创建两个类来关注页面。
class Recruiter implements Follower {
String company;
// Rest is the same as User class
}
class Official implements Follower {
String designation;
// Rest is the same as User class
}
让我们在主类中测试这种模式。首先创建一个页面并添加一个新的帖子。
Page page = new Page();
page.addNewPost("I am feeling lucky!");
目前还没有人关注页面,所以不会通知任何人。现在,一个普通用户关注了该页面并添加了另一篇帖子。
User user = new User(page);
page.addNewPost("It's a beautiful day!");
现在,用户将收到通知并输出以下内容。
Latest post seen by a normal user: It's a beautiful day!
接下来,招聘人员和官方人员也关注了该帖子。
Recruiter recruiter = new Recruiter(page);
Official official = new Official(page);
page.addNewPost("Ready to go for a run!!");
所有三个关注者都会得到这个活动的通知。
Latest post seen by a normal user: Ready to go for a run!!
Latest post seen by a recruiter: Ready to go for a run!!
Latest post seen by an official: Ready to go for a run!!
3、策略模式
我们想要为一些船只添加潜水功能。继承和方法重写这两种方法都不能满足我们的需求。我们需要将正在更改的代码与已经存在的代码分离开来。唯一正在更改的部分是dive()
行为,因此我们创建一个Diveable
接口并创建另外两个实现它的类。
interface Diveable {
public void dive();
}
class DiveBehaviour implements Diveable {
public void dive() {
// Implementation here
}
}
class NoDiveBehaviour implements Diveable {
public void dive() {
// Implementation here
}
}
现在,在Boat
类中,创建一个接口的引用变量,并拥有一个执行performDive()
方法,该方法调用这个dive()
方法。
abstract class Boat {
Diveable diveable;
void sway() { ... }
void roll() { ... }
abstract void present();
public void performDive() {
diveable.dive();
}
}
FishBoat
和DinghyBoat
类不应该具有潜水行为,因此它们将继承NoDiveBehaviour
类。我们来看看具体实现方法。
class FishBoat extends Boat {
...
public FishBoat() {
diveable = new NoDiveBehaviour();
}
...
}
当diveable
引用变量实例化为NoDiveBehaviour
对象时,FishBoat
类从该类继承dive()
方法。
对于新的SubBoat
类,可以继承一个新的行为。
class SubBoat extends Boat {
...
public FishBoat() {
diveable = new DiveBehaviour();
}
...
}
现在,让我们测试功能
Boat fishBoat = new FishBoat();
fishBoat.performDive();
当调用performDive()
时,它调用NoDiveBehaviour
类的dive
方法。
Boat subBoat = new SubBoat();
subBoat.performDive();
这会执行完全不同的操作,因为它调用实际的潜水行为。因此,我们的新船变成了潜艇并潜水了下去。
4、装饰器模式
有时候,你想要对你的功能做一些修改。但在这样做时,你需要确保不改变现有的功能。
我们以一个Car
类为例,该类被两个类Ford
和Audi
扩展。它有一个build()
方法以及其他重要的方法。这个方法是抽象的,因为每辆车都有自己的实现。
abstract class Car {
abstract void build();
}
class Ford extends Car {
public void build() {
System.out.println("Ford built");
}
}
class Audi extends Car {
public void build() {
System.out.println("Audi built");
}
}
一切都运行正常。然而,客户想要进行一些修改,比如添加彩色车灯、添加尾翼或添加氮气。你将如何进行这些添加?
一种方法是为具有这些修改的汽车创建不同的子类,如AudiWithSpoiler
、FordWithNitrous
等。但你可以看到这很快就会变成一个大问题。可能的组合数量是没有限制的。
另一种方法是将每个mod
作为一个实例变量,并在所需的mod
上调用build()
。然而,对于每个新的mod
,你都需要不断修改现有的代码,从而增加引入错误的可能性。
有一种更好、更灵活的方法。你可以为每个mod定义单独的类,并将你的汽车包装在该对象周围。这是什么意思?你很快就会明白。
创建一个扩展Car的CarModifications
类,将这个类称为mod类
。
abstract class CarModifications extends Car {
Car car;
public CarModifications(Car car) {
this.car = car;
}
}
通过在CarModifications
内创建一个Car
对象,你将Car
包装在mod
类内。mod
类是一个抽象类,并由另外三个类扩展:ColorLight
、Spoiler
和Nitrous
。
class Spoiler extends CarModifications {
public Spoiler(Car car) {
super(car);
}
public void build() {
car.build();
addSpoiler();
}
void addSpoiler() {
System.out.println("Spoiler built");
}
}
这是一个特定的mod
类,它包装了Car
。它通过先构建汽车,然后向其添加一个尾翼来实现build()
方法。另外两个mod
类也有类似的实现。
现在,让我们测试这个模式。我们将创建一个Audi
并为它添加一个尾翼。
Car audi = new Audi();
Car audiWithSpoiler = new Spoiler(audi);
audiWithSpoiler.build();
在创建Car
对象之后,你可以使用相同的实例创建一个新的带有附加尾翼的Car
对象。调用build()
方法执行该模式,得到以下输出。
Audi built
Spoiler built
如果你想要一个带有氮气的车,再创建另一个Car对象。
Car audiWithMods = new Nitrous(audiWithSpoiler);
audiWithMods.build();
输出:
Audi built
Spoiler built
Nitrous built
推荐书单
《深入理解Java高并发编程》
《深入理解Java高并发编程》致力于介绍Java高并发编程方面的知识。由于多线程处理涉及的知识内容十分丰富,因此介绍时必须从Java层面的讲解一直深入到底层的知识讲解。为了帮助读者轻松阅读本书并掌握其中知识,本书做了大量基础知识的铺垫。在第1篇基础知识储备中,主要介绍计算机原理、并发基础、常见语言的线程实现、Java并发入门、JUC之Java线程池、JUC之同步结构、Java NIO详解等内容。在第2篇深入Java并发原理中,详细介绍了JUC包中所有使用的原子类的原理与源码实现;非常关键且容易出错的volatile关键字的原理,从Java、JVM、C、汇编、CPU层面对其进行详细讲解;synchronized在JVM中获取锁和释放锁的流程;JUC包的核心结构——AQS的原理与源码实现,通过逐方法、逐行的解释,帮助读者彻底掌握AQS中提供的获取锁、释放锁、条件变量等操作的实现与原理。最后,详细介绍了JVM中JNI的实现原理,将Java Thread对象中的所有方法在JVM层面的实现流程进行了详细描述,以帮助读者在使用这些方法时,知道底层发生了什么,以及发生异常时如何从容解决问题。
购买链接:https://item.jd.com/13523064.html
精彩回顾