前语
规划形式就像是编程界的“宝藏”,是一些经过验证和总结的最佳实践,能够协助咱们更好地安排代码、解决问题,并进步代码的可读性、可重用性和可保护性。而这些“宝藏”是由一些很厉害的程序员们历经多年的开发实践和经历总结得来的。他们在面对各种问题时,都会考虑怎么运用最有效的办法来解决,一同也在这个进程中不断总结优异的编程思维和办法论,然后形成了各种规划形式。
当然,这些规划形式并不是银弹,不能解决一切问题,只需在特定的场景下才能发挥出它们的优势。所以,作为程序员,咱们需求依据详细状况挑选合适的形式,并将其灵敏运用,才能真实发挥规划形式的效果。
工厂形式
假定你是一个巧克力工厂的老板,你需求出产不同口味的巧克力。你能够挑选自己亲自动手制造每个口味的巧克力,但这样你既辛苦又功率低下。那么,该怎么办呢?
这时分,你能够雇佣一些巧克力师傅(详细工厂类),让他们专门负责制造不同口味的巧克力(详细产品类)。而你只需求供给原材料(工厂办法)和制造巧克力的流程(笼统父类),就能够让巧克力师傅们按照你的要求制造出口感和口味都完美的巧克力。
这样,当你需求新增一种口味的巧克力时,只需求再雇佣一个会制造这种口味的巧克力师傅,并供给对应的原材料和制造流程即可。这样,你就无需亲自动手,也能够轻松地扩展巧克力的品种了。
总归,工厂形式就像是一个巧克力工厂,它为咱们供给了一种便利、高效、可扩展的巧克力出产办法,让咱们能够轻松制造出各种口味的甘旨巧克力。
首要,咱们需求界说一个笼统的巧克力类 Chocolate,它有一个制造巧克力的办法 make()。
public abstract class Chocolate {
public abstract void make();
}
然后,咱们界说详细的巧克力类,比方牛奶巧克力(MilkChocolate)和黑巧克力(DarkChocolate),它们承继自 Chocolate 类,并完结了 make() 办法。
public class MilkChocolate extends Chocolate {
@Override
public void make() {
System.out.println("制造牛奶巧克力");
}
}
public class DarkChocolate extends Chocolate {
@Override
public void make() {
System.out.println("制造黑巧克力");
}
}
接下来,咱们界说一个笼统的巧克力工厂类 ChocolateFactory,它有一个制造巧克力的笼统工厂办法 makeChocolate()。
public abstract class ChocolateFactory {
public abstract Chocolate makeChocolate();
}
然后,咱们能够界说详细的巧克力工厂类,比方牛奶巧克力工厂(MilkChocolateFactory)和黑巧克力工厂(DarkChocolateFactory),它们都承继自 ChocolateFactory 类,并完结了 makeChocolate() 办法。这样,每个详细工厂就能够出产对应品种的巧克力。
public class MilkChocolateFactory extends ChocolateFactory {
@Override
public Chocolate makeChocolate() {
return new MilkChocolate();
}
}
public class DarkChocolateFactory extends ChocolateFactory {
@Override
public Chocolate makeChocolate() {
return new DarkChocolate();
}
}
“
最终,咱们就能够在客户端运用巧克力工厂来出产不同品种的巧克力了。
public class Client {
public static void main(String[] args) {
// 创立牛奶巧克力工厂
ChocolateFactory milkFactory = new MilkChocolateFactory();
// 出产牛奶巧克力
Chocolate milkChocolate = milkFactory.makeChocolate();
// 制造巧克力
milkChocolate.make();
// 创立黑巧克力工厂
ChocolateFactory darkFactory = new DarkChocolateFactory();
// 出产黑巧克力
Chocolate darkChocolate = darkFactory.makeChocolate();
// 制造巧克力
darkChocolate.make();
}
}
以上便是工厂形式的一个简略示例代码,经过笼统父类、详细子类和笼统工厂类、详细工厂类的组合关系,完结了一种高效、可扩展、易于保护的方针创立办法。
这个时分或许有小伙伴问:
然并卵?我怎么没看出来这有啥用啊,而且好像咱们日常开发便是这样啊?
没错!
工厂形式是一种常用的软件规划形式,在项目开发中被广泛运用。工厂形式的首要思维是将方针的创立进程封装起来,以使得程序愈加灵敏和易于保护。
在项目中运用工厂形式能够带来以下几个长处:
降低耦合度:经过工厂形式,客户端代码不需求知道详细的产品类,只需求知道它们的笼统接口即可。这样能够降低客户端与详细产品之间的依赖关系,进步体系的可扩展性和可保护性。
进步代码复用性:工厂形式经过对方针创立的进程进行封装,能够让多个客户端同享同一个方针实例。这样能够防止重复创立方针,进步体系的功用,而且能够进步代码的复用性。
简化方针的创立进程:由于工厂形式将方针的创立进程封装起来,客户端代码无需关怀方针的创立进程,然后简化了代码的编写进程。
因而,工厂形式现已成为项目开发中的常用规划形式之一,运用广泛。无论是在面向方针编程还是函数式编程中,都能够看到工厂形式的运用。
作者
其实咱们能够把工厂形式与Java中的封装思维进行联合回忆,与Java封装数据于类中不同的是它将方针的创立进程封装在一个工厂类中。客户端只需经过工厂类来获取所需的方针实例,而无需了解方针的详细创立进程和内部完结。这样就完结了对方针创立进程的封装,一同进步了代码的可读性和可保护性。
因而,封装和工厂形式都是对完结细节进行封装的战略,它们的意图都是进步代码复用性、简化调用者的运用进程,一同防止直接露出完结细节带来的安全问题。尽管封装和工厂形式的完结机制不同,但它们的方针是共同的,都是为了进步代码质量和开发功率。
单例形式
单例形式就像是一个特别抠门的大老板,它只让你在整个公司里创立一个实例,其他地方都不行。这样做有什么好处呢?首要,它能够确保在整个体系中只需一个特定类型的方针,这样能够防止重复创立方针形成的内存糟蹋和功用问题。其次,由于单例形式只答应创立一个实例,所以不同的代码段获取到的都是同一个实例,这样能够确保数据的共同性和正确性,便利咱们进行资源同享和数据交互。
假如你还不了解,那么能够幻想一下自己是一个卖糖葫芦的小贩,而单例就像是你手里的糖葫芦篮子,你一直只需一个篮子,一切的顾客都从这个篮子里拿糖葫芦。这样能够确保一切的顾客都买到了相同的糖葫芦,而且你也用最少的资源去完结了生意。
当然,这只是一个简略的比方,单例形式的详细完结还需求考虑线程安全等问题,可是信任经过这个比方,您现已开始了解了单例形式的概念和效果
单例形式能够确保一个类只需仅有的实例,并供给大局拜访点。
在单例形式中,一般会将结构函数声明为私有办法,以防止直接创立类的实例。一同,该类还会供给一个静态办法(一般称之为getInstance()),以便调用者能够获取类的仅有实例。
public class Singleton {
private static Singleton instance;
private Singleton() {
// 私有结构函数
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
在上面的代码中,Singleton是一个单例类,它包括一个私有结构函数和一个公共静态办法getInstance()。当第一次调用getInstance()时,会创立Singleton类的实例并将其存储在instance变量中,随后的调用都将回来同一个实例。这样,就能够确保Singleton类的一切方针均为同一实例,而且能够经过getInstance()办法进行大局拜访。
单例形式的首要效果是确保一个类只需仅有的实例,并供给大局拜访点,使得这个实例能够被整个体系中的其他方针所同享和拜访。
在某些状况下,假如咱们需求确保某个类的实例只需一个,例如装备信息类、日志记载器类等,那么运用单例形式就十分合适。由于单例形式能够确保某个类的实例永久只需一个,因而能够防止不用要的内存分配和资源消耗,进步体系的功用和功率。
别的,运用单例形式还能够进步代码的可保护性和可扩展性。假如在未来需求对该类进行修正或拓展,只需修正该类的代码即可,无需修正体系中许多的代码,减少了保护成本。
总而言之,单例形式在软件体系中具有广泛的运用,能够进步体系的功用、功率、可保护性和可扩展性
作者
回忆了解单例形式能够从以下几个方面考虑:
- 仅有性:单例形式的首要特点是确保某个类只需仅有的实例。
- 大局拜访:单例形式供给了大局拜访点,使得该实例能够被整个体系中的其他方针所同享和拜访。
- 私有结构函数:单例形式一般将结构函数声明为私有办法,以防止直接创立类的实例。
咱们在日常开发中,的确会常常运用到单例形式。例如装备信息类、日志记载器类等,它们都需求确保只需一个实例存在,而且能够被整个体系所同享和拜访。在这些状况下,运用单例形式就十分合适。
关于装备类来说,咱们能够经过单例形式完结,将装备文件中的数据读取到内存中,并保存在一个仅有的实例中,在体系中需求拜访装备数据时,直接经过该实例进行拜访即可。这样能够防止重复读取装备文件,进步体系功用和功率。
综上所述,单例形式是一个十分常用且实用的规划形式,在咱们的日常开发中也常常会用到。经过了解其核心思维和运用场景,咱们能够更好地掌握和运用该形式。
那么为什么咱们常常编写的装备类信息中 并没有体现出单例形式呢?
这是由于这个装备类所回来的实例是由Spring容器管理的,而Spring默许状况下会将它们的效果规模设置为“单例”(singleton),也便是说,在整个运用程序中只会创立一次这些实例,而且能够被一切需求它们的方针所同享和拜访。
因而,尽管代码中并没有显式地运用单例形式,可是在Spring容器的管理下,这些实例的确表现出了单例形式的特点。
调查者形式
假定你是某个小区的物业管理员,需求随时知道小区里居民的动态,比方他们搬进、搬出、有什么新的需求等。可是,假如每次都要一个一个去问居民,那工作量也太大了吧!
这时分,你能够运用调查者形式,将自己界说成调查者,居民们界说成被调查者。当有人搬进、搬出、或许有新的需求时,居民们就会发出告知,这些告知就相当于调查者形式中的事情。
而调查者(也便是你)则会及时接纳到这些事情,并进行相应的处理,比方派人去帮忙搬迁,安排维修人员修补问题等等。
这样一来,你就不用一个一个去问居民的状况了,只需求等候居民们主动告知你就好了,工作功率也能进步不少呢!
首要,咱们需求界说两个角色:被调查者和调查者。被调查者需求保护一个调查者列表,当自己状况产生改动时,需求告知一切调查者。调查者则需求完结一个接纳告知的办法。
import java.util.ArrayList;
import java.util.List;
interface Subject { // 被调查者接口
void attach(Observer observer);
void detach(Observer observer);
void notifyObservers();
}
class ConcreteSubject implements Subject { // 详细被调查者
private List<Observer> observers = new ArrayList<>();
private String state;
public void attach(Observer observer) {
observers.add(observer);
}
public void detach(Observer observer) {
observers.remove(observer);
}
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(this);
}
}
public void setState(String state) {
this.state = state;
notifyObservers();
}
public String getState() {
return state;
}
}
interface Observer { // 调查者接口
void update(Subject subject);
}
class ConcreteObserver implements Observer { // 详细调查者
private String name;
public ConcreteObserver(String name) {
this.name = name;
}
public void update(Subject subject) {
System.out.println(name + " 收到了消息: " + ((ConcreteSubject)subject).getState());
}
}
在被调查者中,咱们界说了 attach
、detach
和 notifyObservers
三个办法,分别用于增加调查者、移除调查者以及告知调查者。当被调查者状况产生改动时,会调用 notifyObservers
办法,告知一切调查者。
然后,在调查者中,咱们界说了一个 update
办法,用于接纳被调查者的告知,并做出相应的处理。
最终,咱们能够界说详细的被调查者和调查者类,来完结咱们需求的功用。
public class ObserverPatternDemo {
public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();
ConcreteObserver observer1 = new ConcreteObserver("张三");
ConcreteObserver observer2 = new ConcreteObserver("李四");
subject.attach(observer1);
subject.attach(observer2);
subject.setState("状况1");
subject.detach(observer1);
subject.setState("状况2");
}
}
在这个比方中,咱们界说了一个详细的被调查者 ConcreteSubject
和一个详细的调查者 ConcreteObserver
。当状况产生改动时,调查者会接纳到告知,并输出相应的提示信息。一同,在这个比方中,咱们还演示了怎么增加和移除调查者的进程。
在完结调查者形式时,需求留意以下几个问题:
调查者列表的线程安全性:假如有多个线程一同进行增加、删去调查者操作,那么需求考虑同步和线程安全问题。
调查者的执行次序:当被调查者状况产生改动时,告知调查者的次序或许会影响成果,请在完结时留意这一点。
怎么记住调查者形式呢?能够从它的名字入手,调查者便是调查某个方针的改动并做出相应反响的人或许物体。调查者形式便是让咱们将这个场景笼统出来,并用代码完结。你能够幻想自己在看电视,而电视机上的遥控器便是一个调查者,当你按下遥控器上的按钮时,电视机就会产生改动,这时遥控器就会接纳到告知并作出相应的反响——比方调理音量、切换频道等等。
作者
在实践项目中,调查者形式运用还是比较多的。举个比方,或许你们公司的 HR 部分需求常常了解职工的状况,包括调动、离任、提升等等。假如每次都要手动去询问职工的状况,那功率太低了。这时分,HR 部分能够作为调查者,职工们则是被调查者,当职工状况产生改动时,HR 部分就能够及时接纳到告知并做出相应的处理。
再举一个比方,在游戏开发中,或许需求完结多个游戏方针之间的协作。比方,当玩家进犯敌人时,敌人需求遭到伤害并或许逝世,一同玩家也会获得必定的分数、金币等奖励。假如每次都要手动去完结这些逻辑,代码量很大,而且也不够灵敏。这时分,咱们能够将玩家和敌人作为被调查者,将计分板、掉落物品等作为调查者,当玩家进犯敌人时,被调查者就会发出告知,一切的调查者就能够及时接纳到告知并做出相应的处理,然后简化代码,并进步了可扩展性。
装修者形式
假定你是一家咖啡店的老板,你想要给顾客供给各种不同口味的咖啡。可是你又不想要过多的产品,由于关于每个口味都树立一个品类实在太繁琐了。
这时分,你能够考虑运用装修者形式。它能够动态地为方针增加新功用,而无需修正原有代码。这样你就能够只保护几种根本的咖啡类型了,然后经过增加装修器来完结各种不同的口味。
详细来说,咱们能够界说一个根底的咖啡方针,并经过承继和组合的办法,来动态地增加各种配料(比方牛奶、糖、巧克力等)。
首要,咱们界说一个 Coffee 接口:
public interface Coffee {
double getCost();
String getDescription();
}
其间 getCost() 办法用于获取咖啡的价格,getDescription() 办法用于获取咖啡的描绘信息。
然后,咱们界说一个根底的咖啡完结类 SimpleCoffee:
public class SimpleCoffee implements Coffee {
public double getCost() {
return 1;
}
public String getDescription() {
return "Simple Coffee";
}
}
接下来,咱们界说一个笼统类 CoffeeDecorator,它一同完结了 Coffee 接口:
public abstract class CoffeeDecorator implements Coffee {
private final Coffee decoratedCoffee;
public CoffeeDecorator(Coffee coffee) {
this.decoratedCoffee = coffee;
}
public double getCost() {
return decoratedCoffee.getCost();
}
public String getDescription() {
return decoratedCoffee.getDescription();
}
}
留意到这个笼统类里边包括了一个私有成员变量 decoratedCoffee,它的类型是 Coffee。这个变量用于保存被装修的 Coffee 方针。
然后,咱们能够经过承继这个笼统类完结各种详细的装修器。比方,牛奶、糖和巧克力:
public class Milk extends CoffeeDecorator {
public Milk(Coffee coffee) {
super(coffee);
}
public double getCost() {
return super.getCost() + 0.5;
}
public String getDescription() {
return super.getDescription() + ", Milk";
}
}
public class Sugar extends CoffeeDecorator {
public Sugar(Coffee coffee) {
super(coffee);
}
public double getCost() {
return super.getCost() + 0.2;
}
public String getDescription() {
return super.getDescription() + ", Sugar";
}
}
public class Chocolate extends CoffeeDecorator {
public Chocolate(Coffee coffee) {
super(coffee);
}
public double getCost() {
return super.getCost() + 0.3;
}
public String getDescription() {
return super.getDescription() + ", Chocolate";
}
}
最终,咱们能够用以下办法来运用装修器形式:
Coffee coffee = new SimpleCoffee();
coffee = new Milk(coffee);
coffee = new Sugar(coffee);
coffee = new Chocolate(coffee);
System.out.println("Coffee Description: " + coffee.getDescription());
System.out.println("Coffee Cost: " + coffee.getCost());
在这个比方中,咱们首要创立了一个根底的咖啡方针 SimpleCoffee,然后经过增加 Milk、Sugar 和 Chocolate 装修器,来生成一个具有多种口味的咖啡方针。经过调用咖啡方针的 getDescription() 和 getCost() 办法,咱们能够获取到咖啡的描绘和价格。
方针的初始状况:被装修的方针在创立时应该处于一个完整的、可用的状况。否则,在增加装修器时或许会出现意想不到的问题。
装修器与被装修方针的接口共同:装修器有必要完结与被装修方针相同的接口,这样才能确保装修器能够替换原始方针,而且客户端代码不需求做任何改动。
装修器之间的次序:假如有多个装修器,其增加次序或许会影响成果。一般来说,先增加的装修器会先起效果,后增加的装修器会顺次叠加。因而,在规划时要细心考虑装修器的次序。
防止过度装修:假如增加过多的装修器,或许会导致代码变得紊乱和难以保护。因而,在规划时要留意防止对方针进行过度装修。
作者
关于装修者形式的运用场景,它的运用规模十分广泛,特别是在需求动态地增加或删去方针功用的状况下。例如:
Java 中的 IO 类库:Java 的 IO 类库就许多运用了装修者形式。比方,咱们能够经过增加缓冲、压缩、加密等装修器来完结不同的文件操作功用。
GUI 编程中的组件:在 GUI 编程中,常常需求动态地扩展或改动组件的显示和行为。这时分就能够运用装修者形式来完结。
Web 开发中的过滤器:Web 运用程序中,常常需求过滤、验证、转化输入或输出数据。这时分就能够运用装修者形式来完结。
日志记载:在日志记载中,能够运用装修者形式来增加或删去记载办法,比方记载到文件、控制台或数据库等。
因而,装修者形式在日常编程中运用十分广泛,是一个很实用的规划形式。
战略形式
幻想一下,你是个餐厅老板,菜单上有各种各样的菜式。可是你发现有些客人或许对辣椒比较敏感,有些客人或许喜爱口味偏甜的菜式,而有些客人则喜爱咸味的菜式。
这时分,假如你为每个客人都准备一份特别的菜单,那工作量肯定会很大,也不太实践。所以,你能够运用战略形式。
战略形式就像是一个菜单中的不同选项,你能够依据客人的口味挑选不同的战略来供给菜式。例如,假如客人喜爱辣味,你能够挑选辣椒酱作为调料;假如客人喜爱甜味,你能够加入糖或蜜糖,假如客人喜爱咸味,你能够多加一点盐等等。
在程序规划中,战略形式便是界说一系列算法,将它们分别封装起来,而且使它们能够彼此替换。这样,在运行时,你能够依据需求挑选不同的算法。就像你能够依据客人的口味挑选不同的调料一样。
首要,咱们界说一个接口 Strategy,用于声明支撑的一切算法:
public interface Strategy {
int calculate(int a, int b);
}
接下来,咱们界说详细的算法类,完结 Strategy 接口:
public class AddStrategy implements Strategy {
@Override
public int calculate(int a, int b) {
return a + b;
}
}
public class SubStrategy implements Strategy {
@Override
public int calculate(int a, int b) {
return a - b;
}
}
public class MulStrategy implements Strategy {
@Override
public int calculate(int a, int b) {
return a * b;
}
}
然后,咱们界说一个上下文方针 Calculator,它持有一个 Strategy 方针引证,并供给一个 setStrategy() 办法,用于切换算法:
public class Calculator {
private Strategy strategy;
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
public int calculate(int a, int b) {
return strategy.calculate(a, b);
}
}
最终,咱们能够在客户端代码中运用这些类进行核算:
Calculator calculator = new Calculator();
// 运用加法算法
calculator.setStrategy(new AddStrategy());
int result = calculator.calculate(10, 5); // 输出 15
// 运用减法算法
calculator.setStrategy(new SubStrategy());
result = calculator.calculate(10, 5); // 输出 5
// 运用乘法算法
calculator.setStrategy(new MulStrategy());
result = calculator.calculate(10, 5); // 输出 50
在上面的代码中,咱们经过 setStrategy() 办法来切换不同的算法,而且能够动态地修正核算器的行为。这便是战略形式的核心思维。
战略形式是一种行为规划形式,它将一组算法封装在独立的类中,并使它们之间能够彼此替换。以下是在实践项目中运用战略形式时需求留意的几点:
将改动隔离:战略形式的一个首要特点是将算法的完结与客户端代码别离,使得算法的完结能够更容易地修正或扩展。因而,在挑选一个算法完结时,需求考虑其或许需求被修正或替换的程度。
统一接口:为了抵达不同算法之间彼此替换的意图,一切算法有必要完结相同的接口。这意味着在增加新算法时,需求确保新算法完结了该接口。
战略挑选:在运行时动态挑选算法的进程中,需求依据详细状况进行权衡和挑选。这或许涉及到功用、可靠性、杂乱度等方面的因素。因而,在挑选算法时,需求归纳考虑各种因素,并作出最优的挑选。
可读性:由于战略形式或许涉及到多个类的交互,因而代码的可读性尤为重要。为了进步代码的可读性,需求采用明晰的命名约好、注释和代码安排办法。
作者
战略形式在实践项目中的运用场景十分广泛,以下是一些常见的运用场景:
算法和规矩:关于需求依据不同条件挑选不同算法或规矩的场景,战略形式能够供给一种简略而灵敏的解决方案。
数据格局转化:在将数据从一种格局转化为另一种格局时,或许需求运用不同的转化器。战略形式能够使得这些转化器彼此替换,然后进步体系的灵敏性和可扩展性。
输入输出处理:当需求处理不同类型的输入或输出时,战略形式能够供给一种统一的接口,并协助咱们挑选最合适的输入或输出办法。
订单处理:在订单处理体系中,依据订单状况、支付办法、配送地址等因素挑选不同的处理办法或许会十分杂乱。战略形式能够使得这些处理办法彼此独立,并更容易地修正或扩展。
游戏开发:在游戏开发中,或许需求运用不同的战略来控制游戏中的角色行为、AI 敌人行为、道具效果等方面。战略形式能够使得这些战略彼此替换,然后进步游戏的可玩性和可扩展性。
战略形式能够了解为一个战略挑选器。
在实践运用中,咱们需求依据不同的状况挑选不同的算法或规矩来抵达最优的效果。这个进程相似于在一个战略挑选器中挑选不同的战略来应对不同的问题。
详细地说,战略形式把一组算法封装在独立的类中,并使它们之间能够彼此替换。这样,当需求运用某种算法时,只需求从战略挑选器中挑选相应的算法即可,而不用关怀算法的详细完结细节。
迭代器形式
迭代器形式就像是一个超市里的购物车。你能够经过购物车把你想要买的产品放进去,然后遍历一遍购物车里边的产品,逐一结算。在这个进程中,你不需求关怀购物车里边的产品详细是哪些,只需求按照次序逐一处理即可。而迭代器形式的完结便是让用户无需知道调集内部的结构,就能够逐一处理调集中的元素。
迭代器形式是一种规划形式,它答应咱们拜访一个容器中的一切元素,而不需求露出容器的内部完结细节。在Java中,迭代器形式一般运用Iterator 接口来完结。
Iterator接口包括三个首要办法:hasNext()、next() 和remove()。其间,hasNext() 办法用于判别是否还有下一个元素, next() 办法用于获取下一个元素, remove() 办法用于从容器中删去当前元素(可选操作)。
import java.util.ArrayList;
import java.util.Iterator;
public class IteratorPatternDemo {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
list.add("Orange");
// 获取迭代器方针
Iterator<String> iterator = list.iterator();
// 遍历容器中的元素
while (iterator.hasNext()) {
String fruit = iterator.next();
System.out.println(fruit);
}
}
}
在上面的代码中,咱们首要创立了一个ArrayList容器,并向其间增加了几个字符串元素。然后,经过调用list的iterator()办法,获得了一个迭代器方针。接着,咱们运用while循环遍历了整个容器,并在循环体内经过iterator的next()办法获取了容器中的每一个元素并打印出来。
这便是迭代器形式在Java中的运用。经过运用迭代器,咱们能够便利地遍历容器中的元素,而且不需求了解容器的内部完结细节。
关于迭代器方针
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
//...
}
在Java中,List、Set和Map等调集类都完结了 Iterable 接口。这意味着,能够经过调用它们的 iterator() 办法来获取一个迭代器方针。
该办法回来一个内部类 Itr 的实例,而该类则完结了 Iterator 接口。由于 Itr 类是ArrayList的内部类,因而它能够直接拜访ArrayList的成员变量。
相似地,在 HashSet 和 HashMap 中都有一个内部类 HashIterator,该类相同完结了 Iterator 接口,用于遍历其间的元素。
因而,当咱们调用 list.iterator() 时,实践上是获取到了一个内部类 Itr 的实例,该实例封装了对 ArrayList 内部结构的拜访办法,并供给了 hasNext()、next() 和 remove() 等办法,以支撑遍历 ArrayList 中的元素。
在运用迭代器形式时,需求留意以下几点:
- 确保容器的数据结构安稳:由于迭代器是根据容器完结的,因而在运用迭代器时要确保容器的数据结构安稳。假如容器的结构产生了改动(如增加、删去元素),则或许会导致迭代器失效或不正确。
- 不要直接修正容器中的元素:尽管迭代器能够经过remove()办法移除当前位置的元素,但不主张直接修正容器中的元素。假如需求修正容器中的元素,最好先将其复制到一个临时变量中进行修正,防止对迭代器形成影响。
- 不要一同运用多个迭代器:由于迭代器是根据容器完结的,因而在运用迭代器时不主张一同运用多个迭代器来遍历同一个容器。这或许会导致某些元素被漏掉或重复遍历同一个元素。
- 留意反常处理:在运用迭代器时,需求留意反常处理。例如,在遍历容器时,假如现已抵达容器的末尾,则 hasNext() 办法会回来 false,此时假如仍然调用 next() 办法,则会抛出 NoSuchElementException 反常。
- 完结自界说迭代器时,应该遵从迭代器接口的标准:假如需求完结自界说迭代器,则应该遵从迭代器接口的标准,包括完结 hasNext()、next() 和 remove() 三个办法,而且要依据详细的容器类型来完结这些办法。
作者
迭代器形式的首要运用场景是需求遍历容器(如列表、树等)中的元素,而且不想露出容器的内部结构给客户端。迭代器形式能够将遍历操作和容器内部结构解耦,然后愈加灵敏地处理容器中的元素。
在实践运用中,迭代器形式常常与其他规划形式一同运用,例如工厂办法形式、调查者形式等。下面是一些典型的运用场景:
- 需求遍历一个容器中的元素,但不想露出容器的内部完结细节给客户端。
- 需求对容器中的元素进行过滤、排序或其他操作。
- 需求一同遍历多个容器中的元素,然后将它们合并到一个容器中。
关于了解迭代器形式,咱们能够把它幻想成一个笼统的“遍历器”,它能够协助咱们逐一拜访容器中的元素,而无需了解容器的详细完结办法。从这个角度上来说,迭代器形式能够看作是一种“遍历器”形式,它将遍历操作和容器的完结别离开来,然后更好地安排代码、进步代码复用性和可保护性。
模板办法形式
假定你要制造一份烤鸡翅的食谱,你需求准备好鸡翅、调料和烤箱等工具。可是,你或许不知道应该先涂什么调料、要烤多长时间、要加多少盐等等。这时分,你能够向专业厨师学习制造烤鸡翅的办法。
专业厨师会告知你,制造烤鸡翅的过程大致如下:
- 先将鸡翅洗净,再用纸巾擦干水分。
- 将鸡翅放入碗中,加入适量的盐、胡椒粉、孜然粉等调料,搅拌均匀。
- 在烤盘上铺上锡纸,将鸡翅摆放在烤盘上,放入预热好的烤箱中,烤20分钟。
- 取出鸡翅,不和翻转,再烤10分钟。
这便是一个简略的制造烤鸡翅的模板办法。每个过程都是固定的,只是其间的详细细节或许有所不同。例如,不同的人或许会用不同的调料、烤箱温度也或许有所不同等等。
在软件开发中,模板办法形式也是相似的一种规划形式。它界说了一个算法的骨架,而将详细完结留给子类来完结。这样,在编写代码时就能够防止重复代码,而且能够灵敏地修正算法的详细完结。
因而,咱们能够将模板办法形式了解为一种“食谱”,其间界说了一个固定的算法流程,而将详细完结留给子类来完结。只需确保算法流程的正确性和安稳性,就能够在详细完结上进行灵敏的调整和扩展。
abstract class AbstractClass {
public void templateMethod() {
// Step 1
operation1();
// Step 2
operation2();
// Step 3
operation3();
}
protected abstract void operation1();
protected abstract void operation2();
protected void operation3() {
System.out.println("AbstractClass.operation3()");
}
}
class ConcreteClass extends AbstractClass {
protected void operation1() {
System.out.println("ConcreteClass.operation1()");
}
protected void operation2() {
System.out.println("ConcreteClass.operation2()");
}
}
public class TemplateMethodDemo {
public static void main(String[] args) {
AbstractClass obj = new ConcreteClass();
obj.templateMethod();
}
}
在上面的代码中,AbstractClass 是笼统类,其间界说了一个 templateMethod() 办法,该办法包括了算法的骨架。详细完结留给子类来完结。
ConcreteClass 则是详细子类,完结了 operation1() 和 operation2() 办法,而且承继了 operation3() 办法的默许完结。
在 main() 办法中,咱们创立了一个 ConcreteClass 的方针,并调用其 templateMethod() 办法。由于 ConcreteClass 承继了 AbstractClass,因而能够运用 templateMethod() 办法的完结。
在程序运行时,输出的成果为:
ConcreteClass.operation1()
ConcreteClass.operation2()
AbstractClass.operation3()
这是由于在 templateMethod() 办法中先调用了 operation1() 和 operation2() 办法,而在默许完结中又调用了 operation3() 办法。
经过运用模板办法形式,咱们能够将算法的骨架和详细完结别离开来,然后更好地安排代码并进步代码的可复用性。
在运用模板办法形式时,需求留意以下几点:
- 确定好模板办法的算法流程:模板办法是整个模板办法形式的核心,因而在运用模板办法形式时,需求先确定好算法流程。一般,算法流程中会包括若干个笼统办法和一个详细办法。
- 笼统办法要求子类有必要完结:在界说笼统办法时,需求留意这些办法有必要由子类来完结,而不能在笼统类中直接完结。假如需求在笼统类中供给默许完结,能够运用详细办法来完结。
- 详细办法可有可无:在界说详细办法时,需求留意这些办法是可有可无的,而且不用定需求被子类掩盖。假如某个过程不需求子类完结,则能够在详细办法中供给默许完结。
- 防止过度规划:在运用模板办法形式时,需求防止过度规划,坚持算法流程简略明了,不要让算法流程变得过于杂乱或难以了解。
- 留意笼统类和详细子类之间的耦合:由于笼统类与详细子类严密相关,因而在运用模板办法形式时,需求留意笼统类和详细子类之间的耦合性。假如笼统类太过于杂乱或难以保护,或许会影响详细子类的开发和保护。
总归,在运用模板办法形式时,需求留意确定好算法流程,并在其间合理地运用笼统办法和详细办法。一同,还需求留意笼统类和详细子类之间的耦合性,坚持代码的简练、灵敏和易于保护。
作者
为了更好地记住模板办法形式,你能够将其看作是一种“模板”或许“骨架”,用于界说算法的流程。 相似于烤鸡翅的食谱,模板办法界说了一个固定的过程,而详细完结留给子类来完结。
在实践工作中,模板办法形式有许多运用场景:
- 操作体系的发动流程:操作体系的发动进程需求执行多个过程,例如加载内核、初始化设备、发动服务等等。这些过程的次序和细节或许会由于不同的操作体系而有所不同,但整个发动流程的逻辑是相同的,能够运用模板办法形式来界说。
- HTTP恳求的处理流程:在Web开发中,HTTP恳求的处理流程也需求执行多个过程,例如解析恳求、调用控制器、烘托模板等等。这些过程的详细完结或许由于不同的框架而有所不同,但处理恳求的流程是相同的,能够运用模板办法形式来界说。
- 数据库拜访的流程:数据库拜访一般需求树立连接、发送查询句子、获取成果集等等。这些过程的细节或许由于不同的数据库而有所不同,但整个拜访流程的逻辑是相同的,能够运用模板办法形式来界说。
总的来说,模板办法形式适用于需求运用固定算法流程,但又期望在详细完结上坚持灵敏和可扩展性的场景。经过运用模板办法形式,咱们能够防止重复代码,而且能够更好地安排代码,进步代码的可读性和可保护性。
总结
写在最终 看到这里你应该发现:没错规划形式是一个笼统的东西,由于它不过便是一些资深工作者所总结的经历;供给了一些通用的解决方案来应对各种软件规划问题、实质上是一些笼统的思维和原则,而不是详细的代码完结或技术细节。
相反 咱们在实践工作中或许也是这么做的,只不过那个时分咱们由于缺乏体系化的知识和理论指导,咱们或许无法精确地描绘出这些解决方案的实质和优缺点; 而看了本篇文章后你会发现 :噢~本来这个就叫单例形式、本来这个便是工厂形式啊。
所以真实的规划形式的学习其实重要的更多的是包括了一个程序员对代码的思维以及经历,假如你想好好的学习规划形式,最重要的其实是多学习一些知名开源项目、并阅览其间的源码,仿照这些知名程序员的架构。
信任我!这会给你带来收获的~
最终最终~
持续找实习中!!!(万一被大佬相中了呢)