RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 927564
Accepted
mygedz
mygedz
Asked:2020-01-02 04:33:19 +0000 UTC2020-01-02 04:33:19 +0000 UTC 2020-01-02 04:33:19 +0000 UTC

平面上和空间中两点之间的距离

  • 772

大家好。我正在学习 C#。编写了一个控制台应用程序来检查平面上和空间中两点之间的距离。一切正常,我没有注意到任何错误,但我修复了那些。请尽可能评价。如果你做错了什么,戳你的鼻子。现在我真的需要这个。

    static void Main(string[] args)
    {
        char selection;
        Double x1, x2, y1, y2, z1, z2, distance;

        Console.WriteLine("1. Расстояние между двумя точками на плоскости.");
        Console.WriteLine("2. Расстояние между двумя точками в пространстве.");
        Console.WriteLine("Выберите нужный пункт (1 или 2):");

        selection = Convert.ToChar(Console.ReadLine());

        switch (selection)
        {
            case '1':
                Console.Write("Введите координаты x1: ");
                x1 = Convert.ToDouble(Console.ReadLine());

                Console.Write("Введите координаты x2: ");
                x2 = Convert.ToDouble(Console.ReadLine());

                Console.Write("Введите координаты y1: ");
                y1 = Convert.ToDouble(Console.ReadLine());

                Console.Write("Введите координаты y2: ");
                y2 = Convert.ToDouble(Console.ReadLine());

                distance = Math.Sqrt(Math.Pow(x2 - x1, 2) + Math.Pow(y2 - y1, 2));
                Console.WriteLine($"Расстояние между двумя точками на плоскости равно {distance}");
                Console.ReadKey();
                break;
            case '2':
                Console.Write("Введите координаты x1: ");
                x1 = Convert.ToDouble(Console.ReadLine());

                Console.Write("Введите координаты x2: ");
                x2 = Convert.ToDouble(Console.ReadLine());

                Console.Write("Введите координаты y1: ");
                y1 = Convert.ToDouble(Console.ReadLine());

                Console.Write("Введите координаты y2: ");
                y2 = Convert.ToDouble(Console.ReadLine());

                Console.Write("Введите координаты z1: ");
                z1 = Convert.ToDouble(Console.ReadLine());

                Console.Write("Введите координаты z2: ");
                z2 = Convert.ToDouble(Console.ReadLine());

                distance = Math.Sqrt(Math.Pow(x2 - x1, 2) + Math.Pow(y2 - y1, 2) + Math.Pow(z2 - z1, 2));
                Console.WriteLine($"Расстояние между двумя точками в пространстве равно {distance}");
                Console.ReadKey();
                break;
            default:
                Console.WriteLine("Вы ввели неверный символ");
                Console.ReadKey();
                break;
        }

    }
}

}

有一个现成的exe,但是这里没有办法附加。如果是这样,您可以在此处下载。

c#
  • 1 1 个回答
  • 10 Views

1 个回答

  • Voted
  1. Best Answer
    tym32167
    2020-01-03T03:54:38Z2020-01-03T03:54:38Z

    当您编写程序、编写代码时,对代码质量、样式和结构的要求可能会有所不同,这取决于许多因素。这就像写一部小说——如果你正在写一个短篇小说,那么你就不必担心人物生活的宇宙、每个人的动机以及其他事情。如果你正在写一部伟大的作品,那么你将不得不预见一切,一个虚构世界的规律,这个世界将发生的事件,你作品的不同部分将紧密交织在一起,作品的整体复杂性会增加.

    我将尝试更详细地解释这一点。如果您不打算开发应用程序,即您发送的代码是最终版本并且您永远不需要对其进行更改,那么一切都井井有条,您的几行代码就可以了。如果您解决课程作业或仅解决培训问题,就会发生这种情况。

    现在让我们想象一下,我们正在编写一个庞大的公司系统的一部分(即,最令人困惑的选项之一),可能有什么要求,可以改变什么?

    因此,首先引起您注意的是您使用代码中的点进行操作 - 二维和三维。在您拥有的当前版本中,一个点由 2 或 3 个变量 - 坐标表示。每次需要存储一个点时,都将其存储为一组变量。对于一个简单的应用程序,这并不重要。对于企业来说 - 这很糟糕,因为

    • 如果在程序的某些其他部分需要使用点,那么您还必须使用 2 个变量,而不是一个,这要困难 2 倍。
    • 总的来说,您已经在任务本身中使用“点”一词进行操作,也就是说,根据任务,这是一个完整的对象。您考虑用户输入一个点,点之间的距离,但是您的代码中没有“点”实体,这使您的代码的阅读者(并且代码的阅读次数多于编写次数,因为代码的可读性是非常重要)努力在代码中查看您的点,以便从您脑海中的变量中收集一个点。

    因此,如果是公司应用程序,我要做的第一件事是选择 2 个实体 - 2d 和 3d 点。它可能看起来像这样:

    public class Point2D
    {
        public double X { get; }
        public double Y { get; }
    
        public Point2D(double x, double y)
        {
            X = x;
            Y = y;
        }
    }
    
    public class Point3D
    {
        public double X { get; }
        public double Y { get; }
        public double Z { get; }
    
        public Point3D(double x, double y, double z)
        {
            X = x;
            Y = y;
            Z = z;
        }
    }
    

    现在,手头有点,我们进一步阅读任务 - 我们需要能够找到点之间的距离。您在 main 函数中正确执行此操作,但我们正在编写一个公司系统 - 这意味着您的应用程序将经过多年开发,也就是说,肯定在某个地方、某个时候、也许另一个程序员需要完全相同的功能 - 计算距离点之间。因此,建议为此编写一个函数。但是这个函数去哪里了?对于最简单的操作,我将创建一个单独的类来处理点或几何图元。我会将类和方法本身设为静态,因为计算点之间的距离是一种在未来不太可能改变的算法,无论我们的应用程序逻辑如何变化。

    public static class GeomertyMath
    {
        public static double Distance(Point2D p1, Point2D p2)
        {
            return Math.Sqrt((p1.X - p2.X) * (p1.X - p2.X) 
                             + (p1.Y - p2.Y) * (p1.Y - p2.Y));
        }
    
        public static double Distance(Point3D p1, Point3D p2)
        {
            return Math.Sqrt((p1.X - p2.X) * (p1.X - p2.X) 
                             + (p1.Y - p2.Y) * (p1.Y - p2.Y) 
                             + (p1.Z - p2.Z) * (p1.Z - p2.Z));
        }
    }
    

    接下来,问题是——我们如何理解我们的方法是正确的?此外,在我们之后编写代码的程序员可能会意外或故意更改我们的公式,使程序无法使用。如何降低这种风险?这就是自动测试发挥作用的地方(在 Google 中搜索的关键字是单元测试、单元测试)。例如,这里有几个使用 NUnit 框架的测试

    [TestFixture]
    public class GeomertyMath_Tests
    {
        [Test]
        public void Distance2D_positive_test()
        {
            var p1 = new Point2D(10, 10);
            var p2 = new Point2D(13, 14);
    
            var expectedResult = 5;
            var actualResult = GeomertyMath.Distance(p1, p2);
    
            var eps = 0.000001;
    
            Assert.True(Math.Abs(expectedResult - actualResult) < eps);
        }
    
        [Test]
        public void Distance2D_zero_test()
        {
            var p1 = new Point2D(0, 0);
            var p2 = new Point2D(0, 0);
    
            var expectedResult = 0;
            var actualResult = GeomertyMath.Distance(p1, p2);
    
            var eps = 0.000001;
    
            Assert.True(Math.Abs(expectedResult - actualResult) < eps);
        }
    }
    

    那么我们有什么?我们有积分,我们有一个经过验证的计算距离的方法。此外,我们需要能够从用户那里读取这些点。也就是说,我们需要一个函数来读取一个点(2d 和 3d)。随着您获得开发经验,您将开始了解某些功能的代码职责等。例如,从控制台读取一个点是一个函数,理论上它不会与点本身的逻辑、距离计算的逻辑或一般的几何运算有任何交叉。也就是说,从控制台读取一个点是一个绝对独立的功能,所以我将为此创建一个单独的类。原则上,我会将我们需要的所有与控制台的交互添加到这个类中。

    public interface IReaderWriter
    {
        Point2D Read2DPoint();
        Point3D Read3DPoint();
        string ReadLine();
        void WriteLine(string line);
    }
    
    public class ConsoleReaderWriter : IReaderWriter
    {
        public Point2D Read2DPoint()
        {
            Console.Write("Введите координаты x: ");
            var x = Convert.ToDouble(Console.ReadLine());
            Console.Write("Введите координаты y: ");
            var y = Convert.ToDouble(Console.ReadLine());
            return new Point2D(x, y);
        }
    
        public Point3D Read3DPoint()
        {
            Console.Write("Введите координаты x: ");
            var x = Convert.ToDouble(Console.ReadLine());
            Console.Write("Введите координаты y: ");
            var y = Convert.ToDouble(Console.ReadLine());
            Console.Write("Введите координаты z: ");
            var z = Convert.ToDouble(Console.ReadLine());
            return new Point3D(x, y, z);
        }
    
        public void WriteLine(string line)
        {
            Console.WriteLine(line);
        }
    
        public string ReadLine()
        {
            return Console.ReadLine();
        }
    }
    

    请注意,该类本身不是静态的,它实现了我们编写的接口。这是有目的的,因为在未来,如果我们不仅要从控制台读取点,而且还要从数据库或 Web 服务或其他一些数据源读取点,那么很容易编写几个从控制台读取只是一种选择的接口的实现。然后,我们将能够运行我们的脚本,同时不仅从控制台读取数据。这也将有助于我们测试我们的场景。

    所以,我们有一个读点器,下一步是什么?接下来,我们有一个带有选择的主要场景和 2 个独立场景,在一个场景中我们读取 2d 点,在另一个场景中 - 3d。让我们以 3 个函数的形式来表示。不要忘记这些脚本是一个单独的功能,与我们代码的其他部分没有任何联系,所以我将把它分成一个单独的类。

    public class MainScenarios
    {
        private readonly IReaderWriter _readerWriter;
    
        public MainScenarios(IReaderWriter readerWriter)
        {
            _readerWriter = readerWriter;
        }
    
        public void Start()
        {
            _readerWriter.WriteLine("1. Расстояние между двумя точками на плоскости.");
            _readerWriter.WriteLine("2. Расстояние между двумя точками в пространстве.");
            _readerWriter.WriteLine("Выберите нужный пункт (1 или 2):");
    
            var selection = Convert.ToChar(_readerWriter.ReadLine());
    
            switch (selection)
            {
                case '1':
                    Read2DScenario();
                    break;
                case '2':
                    Read3DScenario();
                    break;
                default:
                    _readerWriter.WriteLine("Вы ввели неверный символ");
                    break;
            }
        }
    
        private void Read2DScenario()
        {
            _readerWriter.WriteLine("Введите первую точку:");
            var p1 = _readerWriter.Read2DPoint();
            _readerWriter.WriteLine("Введите вторую точку:");
            var p2 = _readerWriter.Read2DPoint();
            var distance = GeomertyMath.Distance(p1, p2);
            _readerWriter.WriteLine($"Расстояние между двумя точками на плоскости равно {distance}");
        }
    
        private void Read3DScenario()
        {
            _readerWriter.WriteLine("Введите первую точку:");
            var p1 = _readerWriter.Read3DPoint();
            _readerWriter.WriteLine("Введите вторую точку:");
            var p2 = _readerWriter.Read3DPoint();
            var distance = GeomertyMath.Distance(p1, p2);
            _readerWriter.WriteLine($"Расстояние между двумя точками в пространстве равно {distance}");
        }
    }
    

    请注意,脚本并不确切知道它们从哪里读取以及从哪里写入值。这个脚本类只传递了一个接口。如何使用它?让我们编写入口点的逻辑:

    class Program
    {
        static void Main(string[] args)
        {
            var readerWriter = new ConsoleReaderWriter();
            var logic = new MainScenarios(readerWriter);
            logic.Start();
            Console.ReadKey();
        }
    }
    

    我们在这里看到,在程序执行的最开始,我们只是准备了我们需要的类并运行它们。这一点,当我们刚刚启动并运行系统时,称为组合根。这里没有逻辑,只有必要的类的准备和它们的启动。

    看来,还有什么可以补充的?但这还不是全部!问题出现了——我们如何确保我们的逻辑、我们的脚本正常工作?答案很明显,我们需要一个脚本测试。这就是我们的stsknaria不知道他们从哪里读取以及他们在哪里写入他们的值的事实将帮助我们。

    让我们编写我们自己的与用户交互的模拟,例如,这个

    public class ReaderWriterMock : IReaderWriter
    {
        private string[] _userInput;
        private int _index = 0;
        private StringBuilder _userOutput = new StringBuilder();
    
        public ReaderWriterMock(string userInput)
        {
            _userInput = userInput.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
        }
    
        public Point2D Read2DPoint()
        {
            return new Point2D(Double.Parse(ReadLine()), Double.Parse(ReadLine()));
        }
    
        public Point3D Read3DPoint()
        {
            return new Point3D(Double.Parse(ReadLine()), Double.Parse(ReadLine()), Double.Parse(ReadLine()));
        }
    
        public string ReadLine()
        {
            return _userInput[_index++];
        }
    
        public void WriteLine(string line)
        {
            _userOutput.AppendLine(line);
        }
    
        public string GetOutput()
        {
            return _userOutput.ToString();
        }
    }
    

    如您所见,在这个类的帮助下,我们将能够在没有人为干预的情况下运行我们的逻辑和测试。也就是说,我们可以编写逻辑测试并运行它们,从而表明我们的逻辑工作正常。这是 2 个测试的示例,第一个测试模拟无效的用户输入,第二个是具有 2 维点的简单脚本。

    [TestFixture]
    public class MainScenariosTests
    {
        [Test]
        public void WrongInputTest()
        {
            var userInput = @"3";
            var readerWriter = new ReaderWriterMock(userInput);
            var scenarios = new MainScenarios(readerWriter);
    
            scenarios.Start();
    
            var userOutput = readerWriter.GetOutput();
            var expected = @"1. Расстояние между двумя точками на плоскости."
                           + Environment.NewLine + "2. Расстояние между двумя точками в пространстве."
                           + Environment.NewLine + "Выберите нужный пункт (1 или 2):"
                           + Environment.NewLine + "Вы ввели неверный символ"
                           + Environment.NewLine;
    
            Assert.AreEqual(expected, userOutput);
        }
    
        [Test]
        public void Scenario2DTest()
        {
            var userInput = @"
    1
    10
    15
    13
    19
    ";
            var readerWriter = new ReaderWriterMock(userInput);
            var scenarios = new MainScenarios(readerWriter);
    
            scenarios.Start();
    
            var userOutput = readerWriter.GetOutput();
            var expected = @"1. Расстояние между двумя точками на плоскости."
                           + Environment.NewLine + "2. Расстояние между двумя точками в пространстве."
                           + Environment.NewLine + "Выберите нужный пункт (1 или 2):"
                           + Environment.NewLine + "Введите первую точку:"
                           + Environment.NewLine + "Введите вторую точку:"
                           + Environment.NewLine + "Расстояние между двумя точками на плоскости равно 5"
                           + Environment.NewLine;
    
            Assert.AreEqual(expected, userOutput);
    
        }
    }
    

    结果,在所有的手势之后,我们有了一个易于扩展和测试的程序。是的,它需要更多的文本,但它的一个单独部分很容易阅读,它的用途很明显,扩展了功能(例如,不是从控制台读取输入数据,而是从数据库或其他地方读取数据) ) 可以很快完成,并且正确性程序的工作是通过测试来证明的。

    参考文献:

    如何编写代码:

    • 完美的代码。硕士课
    • 干净的代码:创建、分析和重构

    Оганизация кода и паттерны

    • Приемы объектно-ориентированного проектирования. Паттерны проектирования
    • SOLID (объектно-ориентированное программирование)
    • Внедрение зависимостей в .NET

    Тестирование

    • Искусство автономного тестирования с примерами на С#
    • Экстремальное программирование. Разработка через тестирование

    Другая литература по c#

    • Книги и учебные ресурсы по C#
    • 2

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    根据浏览器窗口的大小调整背景图案的大小

    • 2 个回答
  • Marko Smith

    理解for循环的执行逻辑

    • 1 个回答
  • Marko Smith

    复制动态数组时出错(C++)

    • 1 个回答
  • Marko Smith

    Or and If,elif,else 构造[重复]

    • 1 个回答
  • Marko Smith

    如何构建支持 x64 的 APK

    • 1 个回答
  • Marko Smith

    如何使按钮的输入宽度?

    • 2 个回答
  • Marko Smith

    如何显示对象变量的名称?

    • 3 个回答
  • Marko Smith

    如何循环一个函数?

    • 1 个回答
  • Marko Smith

    LOWORD 宏有什么作用?

    • 2 个回答
  • Marko Smith

    从字符串的开头删除直到并包括一个字符

    • 2 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5