Refactoring 这种控制流动结构的做法是否良好?
我想重新编写一个嵌套Refactoring 这种控制流动结构的做法是否良好?,refactoring,control-flow,Refactoring,Control Flow,我想重新编写一个嵌套if语句过多的方法 我提出了这一方法,希望得到您的意见: public void MyMethod() { bool hasFailed = false; try { GetNewOrders(out hasFailed); if(!hasFailed) CheckInventory(out hasFailed); if(!hasFailed) PreOrder(out has
if
语句过多的方法
我提出了这一方法,希望得到您的意见:
public void MyMethod()
{
bool hasFailed = false;
try
{
GetNewOrders(out hasFailed);
if(!hasFailed)
CheckInventory(out hasFailed);
if(!hasFailed)
PreOrder(out hasFailed);
// etc
}
catch(Exception ex)
{
}
finally
{
if(hasFailed)
{
// do something
}
}
}
我觉得这个不太好。hasFailed变量的使用真的不好。如果GetNewOrders因异常而失败,例如,您将以hasFailed=false结束catch块
与这里的其他答案相反,我相信布尔“hasfiled”可能有合法的用法,但并不例外。但我真的不认为应该将这样的条件混合到异常处理程序中 不太可能。如果“catch”块捕捉到错误,您的方法应该引发异常。我做过类似的事情,但没有异常处理:
BOOL ok = CallSomeFunction();
if( ok ) ok = CallSomeOtherFunction();
if( ok ) ok = CallYetAnotherFunction();
if( ok ) ok = WowThatsALotOfFunctions();
if( !ok ) {
// handle failure
}
或者如果你想变得聪明:
BOOL ok = CallSomeFunction();
ok &= CallSomeOtherFunction();
ok &= CallYetAnotherFunction();
...
如果仍然使用异常,为什么需要
hasfiled
变量?而不评论try/catch内容,因为我真的不知道那里发生了什么,我会更改它,使被调用的方法返回true/false以获得成功,然后根据布尔短路来检查它们,以避免在前面的方法失败时调用后面的方法
public void MyMethod()
{
bool success = false;
try
{
success = GetNewOrders()
&& CheckInventory()
&& PreOrder();
// etc
}
catch(Exception ex) { }
finally
{
if(!success)
{
}
}
}
我知道我可能会重复一些帖子:
还有什么问题?您还可以使用惰性求值(a()&&b()
)来链接方法,但这取决于状态作为返回值给出,无论如何,返回值的可读性更高
我不同意你应该引发异常的说法,因为如果程序出现错误或者程序因为操作而进入异常状态,那么应该引发异常。异常不是业务逻辑。我会这样做:
public void MyMethod()
{
bool success = false;
try
{
GetNewOrders(); // throw GetNewOrdersFailedException
CheckInventory(); // throw CheckInventoryFailedException
PreOrder(); // throw PreOrderFailedException
success = true;
}
catch( GetNewOrdersFailedException e)
{
// Fix it or rollback
}
catch( CheckInventoryFailedException e)
{
// Fix it or rollback
}
catch( PreOrderFailedException e)
{
// Fix it or rollback
}
finally
{
//release resources;
}
}
扩展异常非常简单
public NewExecption:BaseExceptionType{}据我所知,这是一个级联步骤的示例,其中,如果第一个和第一个及第二个都有效,则将执行第二个和第三个步骤,即返回hasfiled==false
使用模板方法和Decorator设计模式可以使代码更加优雅
您需要一个接口、具体实现、抽象类和抽象类的几个子类
public interface Validator {
public boolean isValid();
}
public class GetNewOrders implements Validator {
public boolean isValid() {
// same code as your GetNewOrders method
}
}
public abstract class AbstractValidator implements Validator {
private final Validator validator;
public AbstractValidator(Validator validator) {
this.validator = validator;
}
protected boolean predicate();
protected boolean isInvalid();
public final boolean isValid() {
if (!this.validator.isValid() && predicate() && isInvalid())
return false;
return true;
}
}
public class CheckInventory extends AbstractValidator {
public CheckInventory(Validator validator) {
super(validator);
}
@Override
public boolean predicate() {
return true;
}
@Override
public boolean isInvalid() {
// same code as your CheckInventory method
}
}
public class PreOrder extends AbstractValidator {
public CheckInventory(Validator validator) {
super(validator);
}
@Override
public boolean predicate() {
return true;
}
@Override
public boolean isInvalid() {
// same code as your PreOrder method
}
}
现在,您的方法看起来更加优雅:
public void MyMethod() {
bool success = false;
try {
Validator validator = new GetNewOrders();
validator = new CheckInventory(validator);
validator = new PreOrder(validator);
success = validator.isValid();
} finally {
if (!success) {
// do something
}
}
}
可以在一行中创建Validator对象,但我更喜欢这种样式,因为它可以明确验证的顺序。在链中创建新的验证链接是对AbstractValidator类进行子类化以及谓词和isInvalid方法的实现。好吧,我不喜欢这样的代码:获取订单列表,然后处理它们,然后在出现错误时停止处理它们,当然它应该跳过该顺序并移动到下一个?唯一完全失败的是数据库(订单源、预订单目的地)死机时。我认为整个逻辑确实有点古怪,但可能是因为我没有你所用语言的经验
try {
// Get all of the orders here
// Either in bulk, or just a list of the new order ids that you'll call the DB
// each time for, i'll use the former for clarity.
List<Order> orders = getNewOrders();
// If no new orders, we will cry a little and look for a new job
if (orders != null && orders.size() > 0) {
for (Order o : orders) {
try {
for (OrderItem i : o.getOrderItems()) {
if (checkInventory(i)) {
// Reserve that item for this order
preOrder(o, i);
} else {
// Out of stock, call the magic out of stock function
failOrderItem(o, i);
}
}
} catch (OrderProcessingException ope) {
// log error and flag this order as requiring attention and
// other things relating to order processing errors that aren't database related
}
}
} else {
shedTears();
}
} catch (SQLException e) {
// Database Error, log and flag to developers for investigation
}
试试看{
//把所有的订单都拿到这里
//可以是批量的,也可以只是一个新订单ID列表,您将调用DB
//为了清晰起见,每次我都会使用前者。
List orders=getNewOrders();
//如果没有新订单,我们会哭一哭,寻找新的工作
if(orders!=null&&orders.size()>0){
对于(订单o:订单){
试一试{
对于(OrderItem i:o.getOrderItems()){
如果(检查库存(i)){
//为这次订购保留那个项目
前序(o,i);
}否则{
//缺货,调用magic缺货函数
失效顺序项(o,i);
}
}
}捕获(OrderProcessingException操作){
//记录错误并将此订单标记为需要注意和
//与订单处理错误相关的其他事项与数据库无关
}
}
}否则{
shedTears();
}
}捕获(SQLE异常){
//数据库错误、日志和标志发送给开发人员进行调查
}
对于一组简单的指令来说,您的新方法并没有那么糟糕,但是当添加额外的步骤时会发生什么呢?您/您是否曾经要求过交易行为?(如果预订单失败怎么办?或者如果预订单失败后的下一步怎么办?)
展望未来,我将使用命令模式:
…并将每个操作封装为实现Execute()和Undo()的具体命令
然后,只需创建一个命令队列并循环,直到失败或出现一个空队列。如果任何步骤失败,只需停止并按照前面命令的顺序执行Undo()。简单。克里斯解决方案是最正确的。但我认为你不应该做超出你需要的事情。解决方案应该是可扩展的,这就足够了
更改参数值是一种不好的做法
永远不要使用空的泛型catch语句,至少要添加注释说明为什么要这样做
使这些方法抛出异常,并在适当的地方处理它们
所以现在它更优雅了:)
我也这么认为。这也缩短了你的代码,因为你不再需要if语句了。而且FWIW,catch块应该对它捕获的异常做一些事情——而不是默默地忽略它们,这有点挫败了异常的意义…@ocdecio——我不同意。异常行为和方法失败之间存在(或可能存在)差异。如果没有订单,GetOrders可能会失败,但如果连接字符串无效,则会导致异常,例如@tvanfosson:“如果没有订单,GetOrders可能会失败”-想想看,没有订单是错误吗???@ocdecio-不,我不这么认为。如果没有参考当前的代码,我想说,如果还没有输入订单,则没有订单是正常情况。如果我正在搜索一个特定的顺序,但没有找到它,这可能是一个例外。在方法的末尾,如果hasfiled=true,我必须执行一些特殊的操作。(bu)
public void MyMethod()
{
try
{
GetNewOrders();
CheckInventory();
PreOrder();
// etc
}
finally
{
// do something
}
}