2014年4月27日 星期日

[Code Review] Team 09 - Hw07


Const.cs
namespace HW7___多邊體計算視窗程式2
{
    static class GeoConst
    {
        public static readonly double Pi = 3.14159;  
    }

    static class MetrialConst
    {
        public static readonly double Density_Al = 2.7;
        public static readonly double Density_Fe = 7.87;
        public static readonly double Density_Pb = 11.3;
        public static readonly double Density_Ice = 0.9;
        public static readonly double Density_Wood = 0.8;
    }
}
IRollable.cs
namespace HW7___多邊體計算視窗程式2
{
    interface IRollable
    {
        double RollDistance();
    }
}
Shape.cs
namespace HW7___多邊體計算視窗程式2
{
    abstract class Shape
    {//此為抽象類別,不可以實體化,因為形狀不是任何一種形狀
        private static int _count = 0;
        public static int Count { get { return _count; } }

        protected double density=0;
        public double Density { get { return density; } }

        //建構子
        public Shape()
        {
            _count++;
        }

        //解構子
        ~Shape()
        {
            _count--;
        }
        
        public double Weight()
        {//重量=體積*密度, 不須另外宣告變數存放
            return Bulk() * density;
        }

        public abstract double Bulk();//體積演算法每種形狀都不同, 留給衍生類實作
    }
}
Ball.cs
namespace HW7___多邊體計算視窗程式2
{
    class Ball : Shape, IRollable
    {
        private static int _count = 0;
        new public static int Count { get { return _count; } }

        //長度參數
        private double _radius = 0;
        
        //建構子
        public Ball(double radius, double density)
        {
            this.density = density;
            this._radius = radius;
            _count++;
        }

        //解構子
        ~Ball()
        {
            _count--;
        }

        public override double Bulk()
        {
            return (4.0 / 3.0) * GeoConst.Pi * _radius * _radius * _radius;
        }

        public double RollDistance()
        {//prototype at Interface
            return _radius * _radius;
        }
    }
}
Cube.cs
namespace HW7___多邊體計算視窗程式2
{
    class Cube : Shape
    {
        private static int _count = 0;
        new public static int Count { get { return _count; } }

        //長度參數
        private double _side = 0;

        //建構子
        public Cube(double side, double density)
        {
            this.density = density;
            this._side = side;
            _count++;
        }

        //解構子
        ~Cube()
        {
            _count--;
        }

        public override double Bulk()
        {
            return _side*_side*_side;
        }
    }
}
Cylinder.cs
namespace HW7___多邊體計算視窗程式2
{
    class Cylinder : Shape,IRollable
    {
        private static int _count = 0;
        new public static int Count { get { return _count; } }

        //長度參數
        private double _radius = 0;
        private double _height = 0;

        //建構子
        public Cylinder(double radius, double height, double density)
        {
            this.density = density;
            this._radius = radius;
            this._height = height;
            _count++;
        }

        //解構子
        ~Cylinder()
        {
            _count--;
        }

        public override double Bulk()
        {
            return GeoConst.Pi * _radius * _radius * _height;
        }

        public double RollDistance()
        {//prototype at Interface
            return _radius * 5;
        }
    }
}
Pyramid.cs
namespace HW7___多邊體計算視窗程式2
{
    class Pyramid : Shape
    {
        private static int _count = 0;
        new public static int Count { get { return _count; } }

        //長度參數
        private double _side = 0;
        private double _height = 0;

        //建構子
        public Pyramid(double side, double height, double density)
        {
            this.density = density;
            this._side = side;
            this._height = height;
            _count++;
        }

        //解構子
        ~Pyramid()
        {
            _count--;
        }

        public override double Bulk()
        {
            double _dSide = _side/1000;
            double _dHeight = _height/1000;
            double _tempBulk=0;

            for (int i=0; i<1000 ; i++ )
            {//從最底下的那片體積往上加,邊長一直縮小
                double _currentSide = _side - (i * _dSide * 0.5); //0.5 使 side 修正到斜邊中點
                _tempBulk += _currentSide * _currentSide * _dHeight;
            }

            return _tempBulk;
        }
    }
}
Form1.cs
namespace HW7___多邊體計算視窗程式2
{
    public partial class Form1 : Form
    {
        private List ListShape = new List();

        public Form1()
        {
            InitializeComponent();
        }

        private double DensityTable(string material)
        {
            if (material == "鐵") return MetrialConst.Density_Fe;
            if (material == "冰") return MetrialConst.Density_Ice;
            if (material == "木材") return MetrialConst.Density_Wood;

            return -9999;
        }

        private void btnAddNewShape_Click(object sender, EventArgs e)
        {
            ///================ 使用者輸入檢查 開始 ================
            bool CheckPass = false; //標記使用者是否通過防呆檢查
            string Forget = "";     //存放使用者忘記輸入的項目
            int DummyInt;           //供int.TryParse()檢查用
            double DummyDouble;     //供double.TryParse()檢查用

            //逐條審查,不可用switch case
            if (null == cbBox_Material.SelectedItem)Forget += " 材質";
            if (null == cbBox_Shape.SelectedItem) Forget += " 形狀";
            if (!int.TryParse(txtBox_AddNewShapeNum.Text, out DummyInt)) Forget += " 數量";
            if (!double.TryParse(txtBox_ShapeParameter1.Text, out DummyDouble)) Forget += " 第1個形狀參數";
            if ((cbBox_Shape.SelectedItem.ToString() == "圓柱體") || (cbBox_Shape.SelectedItem.ToString() == "金字塔"))
            {
                if ("" == txtBox_ShapeParameter2.Text) Forget += " 第2個形狀參數";
            }

            //拋出檢查結果. 若無錯誤就Pass
            if ("" != Forget)
                MessageBox.Show("你忘記輸入" + Forget + " !\a");
            else
                CheckPass = true;//無錯誤
            ///================ 使用者輸入檢查 結束 ================

            //若是沒有通過防呆檢查,以下這個迴圈會被by pass
            for(int i=0;CheckPass && i<(Convert.ToInt16(txtBox_AddNewShapeNum.Text));i++)
            {
                double _localDensity = DensityTable(cbBox_Material.Text);//取得選單所選之密度數值

                switch (cbBox_Shape.SelectedItem.ToString())
                {
                    case "球":
                        {
                            double _tempRadius = Convert.ToDouble(txtBox_ShapeParameter1.Text);
                            ListShape.Add(new Ball(_tempRadius, _localDensity));
                            txtBox_ShapeList1.AppendText("Ball \t"
                                + "半徑=" + Convert.ToString(_tempRadius) + " "
                                + "\n");
                        }
                        break;

                    case "正方體":
                        {
                            double _tempSide = Convert.ToDouble(txtBox_ShapeParameter1.Text);
                            ListShape.Add(new Cube(Convert.ToDouble(txtBox_ShapeParameter1.Text), _localDensity));
                            txtBox_ShapeList1.AppendText("Cube \t"
                                + "邊長=" + Convert.ToString(_tempSide) + " "
                                + "\n");
                        }
                        break;

                    case "圓柱體":
                        {
                            double _tempRadius = Convert.ToDouble(txtBox_ShapeParameter1.Text);
                            double _tempHeight = Convert.ToDouble(txtBox_ShapeParameter2.Text);
                            ListShape.Add(new Cylinder(_tempRadius, _tempHeight, _localDensity));
                            txtBox_ShapeList1.AppendText("Cylinder \t"
                                + "半徑=" + Convert.ToString(_tempRadius) + " "
                                + "高度=" + Convert.ToString(_tempHeight) + " "
                                + "\n");
                        }
                        break;

                    case "金字塔":
                        {
                            double _tempSide = Convert.ToDouble(txtBox_ShapeParameter1.Text);
                            double _tempHeight = Convert.ToDouble(txtBox_ShapeParameter2.Text);
                            ListShape.Add(new Pyramid(_tempSide, _tempHeight, _localDensity));
                            txtBox_ShapeList1.AppendText("Pyramid \t"
                                + "邊長=" + Convert.ToString(_tempSide) + " "
                                + "高度=" + Convert.ToString(_tempHeight) + " "
                                + "\n");
                        }
                        break;
                }
            }
        }

        private void cbBox_Shape_SelectedIndexChanged(object sender, EventArgs e)
        {//顯示介面控制, 當使用者改變 形狀 時, 動態更動UI

            switch (cbBox_Shape.SelectedItem.ToString())
            {
                case "球":
                    {
                        labelParameter1.Text = "直徑"; txtBox_ShapeParameter1.Visible = true;
                        labelParameter2.Text = ""; txtBox_ShapeParameter2.Visible = false;
                    }
                    break;

                case "正方體":
                    {
                        labelParameter1.Text = "邊長"; txtBox_ShapeParameter1.Visible = true;
                        labelParameter2.Text = ""; txtBox_ShapeParameter2.Visible = false;
                    }
                    break;

                case "圓柱體":
                    {
                        labelParameter1.Text = "直徑"; txtBox_ShapeParameter1.Visible = true;
                        labelParameter2.Text = "高"; txtBox_ShapeParameter2.Visible = true;
                    }
                    break;

                case "金字塔":
                    {
                        labelParameter1.Text = "邊長"; txtBox_ShapeParameter1.Visible = true;
                        labelParameter2.Text = "高"; txtBox_ShapeParameter2.Visible = true;
                    }
                    break;
            }
        }

        private void btnCalcShapeParameter_Click(object sender, EventArgs e)
        {
            txtBox_ShapeList2.Clear();

            for (int i = 0; i < ListShape.Count ; i++)
            {
                txtBox_ShapeList2.AppendText( ListShape[i].GetType().Name + "\t "
                                            + "密度:" + ListShape[i].Density.ToString("f2") + "\t "
                                            + "體積:" + ListShape[i].Bulk().ToString("f2") + "\t "
                                            + "重量:" + ListShape[i].Weight().ToString("f2") + "\t "
                                            + "\n");
            }
        }

        private void btnCalcRollDistance_Click(object sender, EventArgs e)
        {
            txtBox_ShapeListForRollDistance.Clear();//清除畫面
            List RollableList = new List();//宣告管理可滾動物件的陣列,每次重新配置

            for (int i = 0; i < ListShape.Count; i++)//檢查每一個由Shape refenece 管理的物件
            {
                //取得 ListShape[i] 的類別資訊
                Ball DummyBall = new Ball(0,0);
                Cylinder DummyCylinder = new Cylinder(0,0,0);
                bool _isBallObj = (ListShape[i].GetType() == DummyBall.GetType());
                bool _isCylinderObj = (ListShape[i].GetType() == DummyCylinder.GetType());
                
                if (_isBallObj || _isCylinderObj)
                {//如果是可以滾動的Shape衍生類,就進來
                    RollableList.Add((IRollable)ListShape[i]); //加入可滾動的List之中 
                    txtBox_ShapeListForRollDistance.AppendText(
                        ((Shape)RollableList[RollableList.Count-1]).GetType().Name + "\t " +
                        "滾動距離:" + RollableList[RollableList.Count-1].RollDistance().ToString("f2") + "\t " + 
                        "\n");
                }
            }//迴圈結束
        }
    }
}

6 則留言:

  1. 提醒一下, namespace最好使用英文.
    這個問題出於你們一開始建立專案時,命名就用"W7___多邊體計算視窗程式2". 雖然用visual studio編譯不會出錯,但是難保之後其他程式用這支專案檔內的類別時會出錯. 所以最好養成以英文命名的習慣.
    其他內容還沒看過,所以就先講到這.

    回覆刪除
    回覆
    1. 林高遠:
      namespace 的名稱是我在建專案時打的專案名稱,IDE就自動當作namespace了,謝謝提醒以後我會注意,不過沒特別狀況的話,應該就不會去改

      刪除
  2. Form1.cs 中第157, 158行的兩個實體化應該可以丟到迴圈外
    應該不需要每個迴圈都做一次

    回覆刪除
    回覆
    1. 林高遠:這個地方是為了最小權限原則&模組化,只有該scope內用到的東西,不宜在scope外宣告;另外程式碼移植時也可以整塊移動,所以這樣做是有必要的,為了以上好處,我寧願讓他每個迴圈都多做一次。

      刪除
    2. 那這樣子我可以理解了,但是我還是覺得這做法不是很好。
      bool _isBallObj = ListShape[i] is Ball ;
      應該是更好的做法,只是現在還沒教到就是了。

      刪除
    3. 我不知道還有這種語法,太好了

      刪除