KWIC—Implicit Invocation
✏️ KWIC—Implicit Invocation
文章目录
- KWIC—Implicit Invocation
- 📝KWIC—Implicit Invocation
- 🧩KWIC
- 🧩核心组件
- 🧩ImplementationScheme
- ⚖️ 隐式调用 vs 显式调用对比
- 🌟 总结
📝KWIC—Implicit Invocation
🧩KWIC
KWIC(Key Word In Context)系统是一个经典的软件架构案例,本实现采用了隐式调用(Implicit Invocation)架构风格。该系统的主要功能是对输入文本进行循环移位并按字母顺序排序输出。
传统的 KWIC 实现通常通过函数调用顺序显式连接模块,例如:输入 → 循环移位 → 排序 → 输出。但这种方式导致模块间耦合度高、可维护性差。
在本系统中,模块间不直接调用,而是通过事件驱动机制进行通信:
- 每当有新输入,
Inputer
会广播InsertToTextLineEvent
CircularShifter
监听此事件,进行移位操作- 移位完成后,广播
InsertToTextLinesEvent
Alphabetizer
接收到该事件后执行排序与输出
这种事件触发机制大大增强了系统的灵活性与扩展性。
模块 | 职责说明 |
---|---|
Inputer | 负责读取文件并逐行生成事件 |
CircularShifter | 监听输入事件,对每一行生成所有移位组合 |
Alphabetizer | 监听移位事件,对所有移位结果按字母序排序 |
Outputer | 接收最终结果,负责控制台打印与文件输出 |
EventManager | 管理监听器注册、注销及事件广播 |
🧩核心组件
- 事件管理器(EventManager):作为系统的中枢,负责协调各组件间的通信
- 维护监听器列表
- 提供监听器注册/注销方法
- 实现事件广播机制
- 输入处理(Inputer):负责读取输入文件
- 逐行读取文本内容
- 触发文本行插入事件
- 循环移位器(CircularShifter):处理文本的循环移位
- 监听文本行插入事件
- 为每行生成所有可能的移位组合
- 触发文本行集合插入事件
- 字母排序器(Alphabetizer):对移位结果进行排序
- 监听移位完成事件
- 对结果进行字母序排序
- 输出最终结果

🧩ImplementationScheme
A third way for styles to
be combined is to
elaborate one level of
package com.wy;
import java.io.IOException;
import java.util.Collections;
public class Alphabetizer implements KWICListener{
private TextLines textlines=null;
@Override
public void handleEvent(KWICEvent event) {
if(event instanceof InsertToTextLinesEvent){
textlines=((CircularShifter) event.getSource()).getTextLines();
Collections.sort(textlines.getLineList());
try {
Outputer.println(textlines);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package com.wy;
import java.util.ArrayList;
public class CircularShifter implements KWICListener{
private TextLines textlines=null;
@Override
public void handleEvent(KWICEvent event) {
if(event instanceof InsertToTextLineEvent){
TextLine textline=((Inputer) event.getSource()).getTextLine();
int number_of_lines=textline.numberOfLines();
ArrayList<ArrayList> exlist=new ArrayList<ArrayList>(0);
ArrayList<String> inlist=new ArrayList<String>(0);
for(int i=0;i<number_of_lines;i++){
for(int j=0;j<textline.numberOfWords(i);j++){
if(j==0) {
inlist.add(textline.getLine(i));
}
else {
inlist.add(textline.shiftwords(i));
}
}
exlist.add(inlist);
inlist=new ArrayList<String>(0);
}
textlines=new TextLines();
for(int i=0;i<number_of_lines;i++){
for(int j=0;j<exlist.get(i).size();j++){
textlines.addLine((String)exlist.get(i).get(j));
}
}
InsertToTextLinesEvent ittles=new InsertToTextLinesEvent(this);
EventManager.broadcast(ittles);
}
}
public TextLines getTextLines(){
return textlines;
}
}
package com.wy;
import java.util.ArrayList;
import java.util.List;
public class EventManager {
// 监听器列表
private static List<KWICListener> listenerList = new ArrayList<KWICListener>();
// 监听器注册方法
public static void addListener(KWICListener listener) {
listenerList.add(listener);
}
// 监听器注销方法
public static void removeListener(KWICListener listener) {
listenerList.remove(listener);
}
// 事件广播方法
public static void broadcast(KWICEvent event) {
for(KWICListener listener : listenerList)
listener.handleEvent(event);
}
}
package com.wy;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class Inputer {
private TextLine textline=null;
public Inputer(FileReader fr) throws IOException {
input(fr);
}
public void input(FileReader fr) throws IOException{
BufferedReader br=new BufferedReader(fr);
textline=new TextLine();
while(br.ready()){
textline.addLine(br.readLine());
InsertToTextLineEvent ittle=new InsertToTextLineEvent(this);
EventManager.broadcast(ittle);
}
}
public TextLine getTextLine(){
return textline;
}
}
package com.wy;
public class InsertToTextLineEvent extends KWICEvent{
public InsertToTextLineEvent(Object source) {
super(source);
}
}
package com.wy;
public class InsertToTextLinesEvent extends KWICEvent{
public InsertToTextLinesEvent(Object source) {
super(source);
}
}
package com.wy;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class KWIC {
public static void main(String args[]) throws IOException{
EventManager.addListener(new CircularShifter());
EventManager.addListener(new Alphabetizer());
FileReader fr=new FileReader("D:\\TXJG\\KWICY\\src\\main\\resources\\input.txt");
Inputer inputer=new Inputer(fr);
}
}
package com.wy;
import java.util.EventObject;
public class KWICEvent extends EventObject{
public KWICEvent(Object source) {
super(source);
}
}
package com.wy;
import java.util.EventListener;
public interface KWICListener extends EventListener{
public void handleEvent(KWICEvent event);
}
package com.wy;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class Outputer {
public static void println(TextLines textlines) throws IOException {
try (BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"))) {
System.out.println("==== 输出开始 ====");
for (int i = 0; i < textlines.numberOfLines(); i++) {
String line = textlines.getLine(i);
bw.write(line);
bw.newLine(); // 写入文件
System.out.println(line); // 同时打印到控制台
}
System.out.println("==== 输出完成 ====");
}
}
}
package com.wy;
import java.util.ArrayList;
public class TextLine {
private ArrayList<String> lines=null;
public TextLine(){
lines=new ArrayList<String>();
}
public String getLine(int index){
return lines.get(index);
}
public ArrayList<String> getLineList(){
return lines;
}
public void addLine(String line){
lines.add(line);
}
public void addLine(int index,String line){
lines.add(index,line);
}
public int numberOfLines(){
return lines.size();
}
public int numberOfWords(int index){
return lines.get(index).split(" ").length;
}
public String shiftwords(int line_index){
String line=this.getLine(line_index);
int temp_index=line.indexOf(' ');
String temp1="";
String temp2="";
if(temp_index!=-1){
temp1=line.substring(0,temp_index);
temp2=line.substring(temp_index+1);
lines.set(line_index,temp2+" "+temp1);
return temp2+" "+temp1;
}
else return null;
}
}
package com.wy;
import java.util.ArrayList;
public class TextLines {
private ArrayList<String> lines=null;
public TextLines(){
lines=new ArrayList<String>();
}
public String getLine(int index){
return lines.get(index);
}
public ArrayList<String> getLineList(){
return lines;
}
public void addLine(String line){
lines.add(line);
}
public void addLine(int index,String line){
lines.add(index,line);
}
public int numberOfLines(){
return lines.size();
}
public int numberOfWords(int index){
return lines.get(index).split(" ").length;
}
}
⚖️ 隐式调用 vs 显式调用对比
对比项 | 隐式调用(当前实现) | 显式调用(传统调用) |
---|---|---|
耦合度 | 低,模块通过事件通信 | 高,模块之间需直接引用调用 |
可扩展性 | 高,新增功能只需注册监听器 | 低,需修改原始模块 |
调试难度 | 相对较高,需跟踪事件链路 | 低,调用路径明确 |
控制流掌握 | 不明显,控制权分散 | 明确,调用顺序可控 |
适用场景 | GUI、分布式系统、日志系统等异步/解耦需求场景 | 简单流程、数据依赖强的系统 |
🌟 总结
特点 | 说明 |
---|---|
松耦合 | 各组件(如 Inputer 、CircularShifter 、Alphabetizer )不直接依赖彼此,而是通过 EventManager 进行事件通信 |
事件驱动 | 采用观察者模式,KWICListener 监听事件,KWICEvent 触发相应处理逻辑 |
职责分离 | 每个模块专注于单一功能(如输入、移位、排序、输出),符合单一职责原则(SRP) |
可扩展性 | 新增处理逻辑只需注册新监听器,无需修改现有代码 |
KWIC 的隐式调用架构通过事件机制实现模块间通信,相比传统分层或管道-过滤器架构,具有更高的灵活性和可维护性。该设计模式适用于需要动态扩展、松耦合的场景,是事件驱动架构(EDA)的典型实践。