Can I set a state within a function?
I have this code:
procedure EstablishCommunication;
var
State : TStates;
Attempts : Byte;
procedure IncAttempts;
begin
Inc(Attempts);
end;
begin
State := stReadDeviceID;
Attempts := 0;
while True do
begin
if Attempts >= MAX_ATTEMPTS then
begin
State := stError;
end;
case State of
stReadDeviceID:
begin
// some code
IncAttempts;
end;
stError:
begin
// Error code
end;
...
...
...
I'd like to put the code that set state to stError within of the procedure IncAttempts, resulting:
procedure EstablishCommunication;
var
State : TStates;
开发者_开发百科 Attempts : Byte;
procedure IncAttempts;
begin
Inc(Attempts);
if Attempts >= MAX_ATTEMPTS then
begin
State := stError;
end;
end;
begin
State := stReadDeviceID;
Attempts := 0;
while True do
begin
case State of
stReadDeviceID:
begin
// some code
IncAttempts;
end;
stError:
begin
// Error code
end;
...
...
...
So, can I move the code to IncAttempts?
Is this a code smell?
If yes, Can you advice me a better way?
I would see this as perfect valid code. I ask myself the following questions when declaring a method inside another. Most of the time I don't do it, but sometimes it's results in better code.
- Will the internal function ever need to change as in a descendant class?
- Can I override External method without calling the internal method and be OK?
- Does the internal function have practical application outside of external method?
- Is the internal function complex enough that it should be unit tested outside the scope of there external method?
If any of the above apply don't use an Internal Method.
However if if you don't have any of the above, and it can remove repeated code and/or simplify the design then you can consider using a internal function.
No real problem with that, should work just fine. You are already modifying another local variable Attempts
so there is no reason why modifying State
should smell more.
I do think you should be careful of using inline functions to much. The code often ends up hard to read/understand.
I would say that the new code have some whiff...
It all depends on how many states you manage in current code, and if the number of states could change in the future. Beware of how and when you set the state, and beware of how and when you check the state.
In the two code snippets you show, there is a minor difference:
In the first, original code, the current state is preserved through the iteration, and the new error-state is set in the beginning of the iteration, and it is always checked.
In the second, refactored code, the state is changed in the middle of the iteration, and it is only altered if the state is stReadDeviceID
.
Now, if the last line in this while True do
-iteration is if State = stError then Break;
, then your first code will run the iteration one more time, changing the state to stError
in the beginning if the iteration. Your second code will exit at the end of the current iteration, and the code in the stError
-section of the case
-statement will never be executed...
If you want to go all the way, and study the GoF's State Design Pattern, then take a look at these pages:
- http://en.wikipedia.org/wiki/State_pattern (no Delphi code...)
- http://sourcemaking.com/design_patterns/state (with Delphi code!)
- http://www.dofactory.com/Patterns/PatternState.aspx (no Delphi code...)
- http://conferences.embarcadero.com/article/32129#_Toc12157322 (with Delphi code!)
精彩评论