如您所见,除了使用的属性外,每个方法的代码都是相同的。例如,X
对Y
和Width
对Height
。无论如何,我是否可以重构代码以使用一个通用的主方法来更改它所使用的属性?
all caps常量是BOX_LINEUP_TOLERANCE
,它的值为0.15f
,并且是由于公司策略所致的所有大写。
这两种方法:
private List<Rectangle[]> GroupRectanglesHorizontally(Rectangle[] rectangles, out int averageGap)
{
List<int> nlGaps = new List<int>();
List<Rectangle[]> rectangleGroups = new List<Rectangle[]>();
for (int bi = 0, bc = rectangles.Length; bi < bc; ++bi)
{
if (rectangleGroups.Any(g => g.Contains(rectangles[bi])))
{
continue;
}
int nTolerance = (int)Math.Ceiling(rectangles[bi].Height * BOX_LINEUP_TOLERANCE);
Rectangle[] baSimiliarHeight = rectangles.Where(b => Math.Abs(b.Y - rectangles[bi].Y) <= nTolerance
&& b.X > rectangles[bi].X)
.ToArray();
if (baSimiliarHeight.Length <= 1)
{
rectangleGroups.Add(new Rectangle[] { rectangles[bi] });
continue;
}
baSimiliarHeight = baSimiliarHeight.OrderBy(b => b.X).ToArray();
int nInitialGap = baSimiliarHeight[0].X - (rectangles[bi].X + rectangles[bi].Width);
int nInitialGapTolerance = (int)Math.Ceiling(nInitialGap * BOX_LINEUP_TOLERANCE);
nlGaps.Add(nInitialGap);
List<Rectangle> rectangleList = new List<Rectangle>();
rectangleList.Add(rectangles[bi]);
rectangleList.Add(baSimiliarHeight[0]);
for (int sbi = 1; sbi < baSimiliarHeight.Length; ++sbi)
{
int nGap = baSimiliarHeight[sbi].X - (baSimiliarHeight[sbi - 1].X + baSimiliarHeight[sbi - 1].Width);
if (nGap <= nInitialGap + nInitialGapTolerance && nGap >= nInitialGap - nInitialGapTolerance)
{
rectangleList.Add(baSimiliarHeight[sbi]);
nlGaps.Add(nGap);
}
else if (nGap > nInitialGap)
{
break;
}
else
{
//Initial gap was larger so first rectangle is in its own group
rectangleList = new List<Rectangle>(new Rectangle[] { rectangles[bi] });
break;
}
}
rectangleGroups.Add(rectangleList.ToArray());
}
averageGap = (int)nlGaps.Average();
return rectangleGroups;
}
private List<Rectangle[]> GroupRectanglesVertically(Rectangle[] rectangles, out int averageGap)
{
List<int> nlGaps = new List<int>();
List<Rectangle[]> rectangleGroups = new List<Rectangle[]>();
for (int bi = 0, bc = rectangles.Length; bi < bc; ++bi)
{
if (rectangleGroups.Any(g => g.Contains(rectangles[bi])))
{
continue;
}
int nTolerance = (int)Math.Ceiling(rectangles[bi].Width * BOX_LINEUP_TOLERANCE);
Rectangle[] baSimiliarWidth = rectangles.Where(b => Math.Abs(b.X - rectangles[bi].X) <= nTolerance
&& b.Y > rectangles[bi].Y)
.ToArray();
if (baSimiliarWidth.Length <= 1)
{
rectangleGroups.Add(new Rectangle[] { rectangles[bi] });
continue;
}
baSimiliarWidth = baSimiliarWidth.OrderBy(b => b.Y).ToArray();
int nInitialGap = baSimiliarWidth[0].Y - (rectangles[bi].Y + rectangles[bi].Height);
int nInitialGapTolerance = (int)Math.Ceiling(nInitialGap * BOX_LINEUP_TOLERANCE);
nlGaps.Add(nInitialGap);
List<Rectangle> rectangleList = new List<Rectangle>();
rectangleList.Add(rectangles[bi]);
rectangleList.Add(baSimiliarWidth[0]);
for (int sbi = 1; sbi < baSimiliarWidth.Length; ++sbi)
{
int nGap = baSimiliarWidth[sbi].Y - (baSimiliarWidth[sbi - 1].Y + baSimiliarWidth[sbi - 1].Height);
if (nGap <= nInitialGap + nInitialGapTolerance && nGap >= nInitialGap - nInitialGapTolerance)
{
rectangleList.Add(baSimiliarWidth[sbi]);
nlGaps.Add(nGap);
}
else if (nGap > nInitialGap)
{
break;
}
else
{
//Initial gap was larger so first rectangle is in its own group
rectangleList = new List<Rectangle>(new Rectangle[] { rectangles[bi] });
break;
}
}
rectangleGroups.Add(rectangleList.ToArray());
}
averageGap = (int)nlGaps.Average();
return rectangleGroups;
}
我也将感谢对代码的一般反馈。
发布于 2016-06-16 16:30:53
您可以使用布尔值作为参数,并根据该参数选择X或Y和宽度或高度。您可以这样声明该方法:
private List<Rectangle[]> GroupRectangles(Rectangle[] rectangles, bool horizontally, out int averageGap)
可以使用水平方法中的代码,并修改属性调用以使用布尔值。例如,您可以将rectangle.X
替换为horizontally ? rectangle.X : rectangle.Y
,将rectangle.Width
替换为horizontally ? rectangle.Width : rectangle.Height
。
然后,可以在以前的方法中使用它:
private List<Rectangle[]> GroupRectanglesHorizontally(Rectangle[] rectangles, out int averageGap)
{
return GroupRectangles(rectangles, true, out averageGap);
}
private List<Rectangle[]> GroupRectanglesVertically(Rectangle[] rectangles, out int averageGap)
{
return GroupRectangles(rectangles, false, out averageGap);
}
或者,您可以使用函数作为参数来选择属性,比如在OrderBy()
中使用lambda表达式。
编辑:对于第一个方法本身:
在以下代码行中:
Rectangle[] baSimiliarHeight = rectangles.Where(b => Math.Abs(b.Y - rectangles[bi].Y) <= nTolerance
&& b.X > rectangles[bi].X)
.ToArray();
它不会得到位于完全相同X位置的矩形。但如果保证所有矩形都不重叠,那就无关紧要了。然后是下面的代码:
if (baSimiliarHeight.Length <= 1)
{
rectangleGroups.Add(new Rectangle[] { rectangles[bi] });
continue;
}
因为以前没有匹配具有相同X位置的矩形,所以baSimiliarHeight
将不包含rectangles[bi]
,因此如果它的长度为1,那么该1元素将是一个不同的矩形,但只有原始矩形才会作为一个组被添加。
在内部for
循环中,最后一个else
的主体只添加初始矩形,而不对以前添加的nGap
s做任何操作,因此循环可以添加一组nGaps
,但只添加初始矩形。
如果你马上得到一个矩形的列表,按照它们的X位置排序,然后从原来的列表中循环出来,你可以在for
循环的正文中选择一些快捷键。
编辑2:
您应该添加更多的描述性名称。我不知道bi
,bc
,nl in nlGaps
等代表什么。对于lambda表达式和循环变量来说,短名称是可以的,但是即使这样,您也应该坚持使用x
、y
、i
、j
或与对象相关的字母。我认为对矩形使用r
比使用b
更有意义。
发布于 2016-06-16 16:52:22
为了保持代码整洁,您可以定义委托:
delegate int AxisLocationDelegate(Rectangle r, bool primaryAxis);
delegate int AxisSizeDelegate(Rectangle r, bool primaryAxis);
然后定义一个统一的GroupRectanglesBy
函数,它将每个委托的实现作为参数:
private List<Rectangle[]> GroupRectanglesBy(Rectangle[] rectangles,
AxisLocationDelegate getLocation, AxisSizeDelegate getSize, out int averageGap)
{
// ...
在主函数中,您可以引用委托函数getLocation
和getSize
,并指定您对主轴还是次轴感兴趣,例如:
int nTolerance = (int)Math.Ceiling(getSize(rectangles[bi], false) * BOX_LINEUP_TOLERANCE);
Rectangle[] baSimiliarMagnitude = rectangles.Where(
b => Math.Abs(getLocation(b, false) - getSize(rectangles[bi], false)) <= nTolerance
&& getLocation(b, true) > getLocation(rectangles[bi], true))
.ToArray();
然后使用选择相关属性的委托的水平/垂直实现进行调用:
private List<Rectangle[]> GroupRectanglesHorizontally(Rectangle[] rectangles, out int averageGap)
{
return GroupRectanglesBy(
rectangles,
(rect, primaryAxis) => primaryAxis ? rect.Location.X : rect.Location.Y,
(rect, primaryAxis) => primaryAxis ? rect.Width : rect.Height,
out averageGap);
}
private List<Rectangle[]> GroupRectanglesVertically(Rectangle[] rectangles, out int averageGap)
{
return GroupRectanglesBy(
rectangles,
(rect, primaryAxis) => primaryAxis ? rect.Location.Y : rect.Location.X,
(rect, primaryAxis) => primaryAxis ? rect.Height : rect.Width,
out averageGap);
}
编辑:如果您感觉特别糟糕,并且希望使用布尔XOR运算符^
,您甚至可以将其简化为一个函数:
private List<Rectangle[]> GroupRectangles(Rectangle[] rectangles, bool vertically, out int averageGap)
{
return GroupRectanglesBy(
rectangles,
(rect, primaryAxis) => primaryAxis ^ vertically ? rect.Location.X : rect.Location.Y,
(rect, primaryAxis) => primaryAxis ^ vertically ? rect.Width : rect.Height,
out averageGap);
}
https://codereview.stackexchange.com/questions/132203
复制相似问题