我正在慢慢地将一个leagacy应用程序转换为MVC风格(目前为新代码和大规模更改的代码)。目前,控制器是按需创建的:
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var firstDigit: UITextField!
@IBOutlet weak var secondDigit: UITextField!
@IBOutlet weak var thirdDigit: UITextField!
@IBOutlet weak var fourthDigit: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "goPrevious", name: "deletePressed", object: nil)
firstDigit.secureTextEntry = true
secondDigit.secureTextEntry = true
thirdDigit.secureTextEntry = true
fourthDigit.secureTextEntry = true
firstDigit.keyboardType = .DecimalPad
secondDigit.keyboardType = .DecimalPad
thirdDigit.keyboardType = .DecimalPad
fourthDigit.keyboardType = .DecimalPad
firstDigit.becomeFirstResponder()
secondDigit.enabled = false
thirdDigit.enabled = false
fourthDigit.enabled = false
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func goPrevious() {
if secondDigit.isFirstResponder() {
secondDigit.enabled = false
firstDigit.enabled = true
firstDigit.becomeFirstResponder()
} else if thirdDigit.isFirstResponder() {
thirdDigit.enabled = false
secondDigit.enabled = true
secondDigit.becomeFirstResponder()
} else if fourthDigit.isFirstResponder() {
fourthDigit.enabled = false
thirdDigit.enabled = true
thirdDigit.becomeFirstResponder()
}
}
// You need to connect each text field to an IBAction (using sent events editing changed) –
@IBAction func firstChanged(sender: UITextField) {
if let digitOne = sender.text.toInt() {
println(digitOne)
sender.enabled = false
secondDigit.enabled = true
secondDigit.becomeFirstResponder()
} else {
sender.text = ""
}
}
@IBAction func secondChanged(sender: UITextField) {
if let digitTwo = sender.text.toInt() {
println(digitTwo)
sender.enabled = false
thirdDigit.enabled = true
thirdDigit.becomeFirstResponder()
} else {
sender.text = ""
}
}
@IBAction func thirdChanged(sender: UITextField) {
if let digitThree = sender.text.toInt() {
println(digitThree)
sender.enabled = false
fourthDigit.enabled = true
fourthDigit.becomeFirstResponder()
} else {
sender.text = ""
}
}
@IBAction func fourthChanged(sender: UITextField) {
if let digitFour = sender.text.toInt() {
println(digitFour)
sender.enabled = false
} else {
sender.text = ""
}
}
}
随着越来越多的代码被转换,我想创建一个提供控制器的主类(FooController := TFooController.Create(GuiProvider, Model);
FooController.EditFoo(); //show Edit-Dialog for Foo
):
TMainController
我的问题是,我需要访问一个控制器内的其他控制器。 (例如,我需要访问FooController内部的BarController)。
知道如何干净利落地解决这个问题吗?或许我的基本想法有缺陷?
我考虑的可能解决方案:
解决方案1(不工作):
我的第一个想法是为每个控制器提供对TMainController的引用:
FooController := MainController.GetFooController();
FooController.EditFoo();
但由于循环单位参考,这不会起作用。因为TMainController需要知道每个其他控制器,并且每个其他控制器都需要对TMainController的引用。
解决方案2(工作但丑陋):
解决方案1的解决方法是为TMainController创建一个基类。然后我可以将这个基类传递给其他控制器,并需要转换为TMainController来访问其他控制器:
//Edit-Dialog for Foo contains a button to edit Bar that belongs to Foo
procedure FooController.PerformActionEditBarOfFoo();
begin
//do something
BarController := FMainController.GetBarController();
BarController.EditBar()
//do some other things
end;
这很有效,但这似乎更像是黑客。
解决方案3(几乎不可能)
另一种解决方案是在创建FooController时提供所有需要的控制器。但是,如果BarController需要其他控制器等,那么这可能是巨大的。
解决方案4(对我来说不可能)
另一个解决方案可能是使用像Spring这样的依赖注入框架。但我不能在现有的代码中促进这种根本性的改变。
答案 0 :(得分:2)
避免循环引用的解决方案之一是使用接口。有几种方法可以做到这一点,有些方法更简单,有些更复杂,具体取决于您现有的代码。
<强> 1。解决方案:定义IMainController
接口并通过全局变量
将您需要在其他控制器中访问的MainContoller
功能定义为完全独立的单元中的接口,并通过全局MainController
变量使用它。这不是最佳解决方案,但它可能是在现有代码库中实现的最简单的解决方案。它还允许稍后以最小的重构实现第二个解决方案。
由于TMainController
从TInterfacedObject
下降,它将被引用计数,因此您必须将其引用存储在接口引用中,以便引用计数正常工作。你不应该自己Free
。
unit MainControllerInterfaceUnit;
interface
uses
FooControllerUnit,
BarControllerUnit;
type
IMainController = interface
function GetFooController: TFooController;
function GetBarController: TBarController;
end;
var
MainController: IMainController;
implementation
uses
MainControllerUnit;
initialization
MainController := TMainController.Create;
end.
unit MainControllerUnit;
interface
uses
FooControllerUnit,
BarControllerUnit,
MainControllerInterfaceUnit;
TMainController = class(TInterfacedObject, IMainController)
public
function GetFooController: TFooController;
function GetBarController: TBarController;
end;
使用界面和全局变量,您可以跳过在MainControllerUnit
中添加FooControllerUnit
,并在您的使用条款中只有MainControllerInterfaceUnit
。
unit FooControllerUnit;
implementation
uses
BarControllerUnit,
MainControllerInterfaceUnit;
procedure TFooController.PerformActionEditBarOfFoo();
var
BarController: TBarController;
begin
BarController := MainController.GetBarController();
BarController.EditBar()
end;
<强> 2。解决方案:定义IMainController
,IFooController
和IBarController
接口,并将MainController
作为参数传递
这是更好的解决方案,因为所有依赖项都被定义为接口。但是,它需要对现有代码进行更多更改。
unit ControllerInterfaceUnit;
interface
type
IFooController = interface
...
end;
IBarController = interface
procedure EditBar;
end;
IMainController = interface
function GetFooController: IFooController;
function GetBarController: IBarController;
end;
implementation
end.
unit MainControllerUnit;
uses
ControllerInterfaceUnit;
TMainController = class(TInterfacedObject, IMainController)
public
function GetFooController: IFooController;
function GetBarController: IBarController;
end;
unit FooControllerUnit;
uses
ControllerInterfaceUnit;
TFooController = class(TInterfacedObject, IFooController)
protected
MainController: IMainController;
public
constructor Create(const AMainController: IMainController);
end;
procedure FooController.PerformActionEditBarOfFoo();
var
BarController: IBarController;
begin
BarController := MainController.GetBarController();
BarController.EditBar()
end;
答案 1 :(得分:2)
以下是使用泛型的解决方案
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<TextView
android:id="@+id/tab_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="11dp"
android:paddingBottom="11dp"
android:textSize="14dp"
android:textStyle="bold"
android:maxLines="1" />
</LinearLayout>
unit MainController;
interface
uses
System.Generics.Collections,
System.SysUtils;
type
TMainController = class
private
FDict: TDictionary<TClass, TObject>;
public
constructor Create;
destructor Destroy; override;
function GetController<T: class>( ): T;
procedure RegisterController<T: class>( AController: T );
end;
implementation
{ TMainController }
constructor TMainController.Create;
begin
inherited;
FDict := TObjectDictionary<TClass, TObject>.Create( [ doOwnsValues ] );
end;
destructor TMainController.Destroy;
begin
FDict.Free;
inherited;
end;
function TMainController.GetController<T>: T;
begin
Result := FDict[ T ] as T;
end;
procedure TMainController.RegisterController<T>( AController: T );
begin
FDict.AddOrSetValue( T, AController );
end;
end.
unit FooController;
interface
uses
MainController;
type
TFooController = class
private
FMainController: TMainController;
public
constructor Create( AMainController: TMainController );
procedure EditFoo;
end;
implementation
uses
BarController;
{ TFooController }
constructor TFooController.Create( AMainController: TMainController );
begin
inherited Create;
FMainController := AMainController;
end;
procedure TFooController.EditFoo;
begin
FMainController.GetController<TBarController>.EditBar;
end;
end.
unit BarController;
interface
type
TBarController = class
public
procedure EditBar;
end;
implementation
{ TBarController }
procedure TBarController.EditBar;
begin
WriteLn( Self.ClassName + '.EditBar called' );
end;
end.