Является ли использование частичных классов хорошей практикой / дизайном? - PullRequest
3 голосов
/ 07 июня 2019

Не могли бы вы сказать, что разделение класса на частичные классы - это способ инкапсулировать ваш код?

Кроме того, почему разделение кода на частичные классы не является частью шаблона проектирования?Я имею в виду, что вы разрабатываете код, разбивая его для лучшего обзора, верно?

General.cs:

/*  SINGLETON DESIGN PATTERN (slides and https://sourcemaking.com/design_patterns/singleton)

     * INTENT
       - Ensure a class has only one instance, and provide a global point of access to it.
       - Encapsulated "just-in-time initialization" or "initialization on first use".

      * ABOUT
       - Make the class of the single instance object responsible for creation, initialization,
       access, and enforcement.

 */
namespace SpaceTaxi_3.States.GameRunning.Parser {
    public partial class LevelParser {

        private static LevelParser instance;

        public static LevelParser GetInstance() {
            return LevelParser.instance ?? (LevelParser.instance = new LevelParser());
        }

        public Dictionary<char, string> Obstacles;
        public Dictionary<char, string> Platforms;
        public Dictionary<char, string> Exits;
        public Dictionary<string, Tuple<int, char, string, int, int, Entity>> Costumer;
        public List<Entity> ObstacleEntities { get; set; }
        public List<Platform> PlatformEntities { get; set; }
        public List<Entity> ExitEntities { get; set; }

        public string[] LevelFile;

        // Wrapper
        public void Load(string fileName) {

            // Catches incorrect files here
            if (ValidMapCheck(fileName)) {
                PlatformEntities = new List<Platform>();
                ExitEntities = new List<Entity>();
                ObstacleEntities = new List<Entity>();

                Platforms = new Dictionary<char, string>();
                Exits = new Dictionary<char, string>();
                Obstacles = new Dictionary<char, string>();
                Costumer = new Dictionary<string, Tuple<int, char, string, int, int, Entity>>();

                LevelFile = ReadFile("Levels", fileName);

                Platforms = GetPlatforms(LevelFile);
                Exits = GetExits(LevelFile);
                Obstacles = GetObstacles(LevelFile);
                Costumer = GetCustomerInfo(LevelFile);

                AddEntities(GetAllTiles(LevelFile));
            }
        }

        /// <summary>
        ///  Checks whether a file exists or not
        /// </summary>
        private void ValidatePath(string file) {
            if (!File.Exists(file)) {
                throw new FileNotFoundException($"Error: " +
                                                $"The path to \"{file}\" does not exist.");
            }
        }

        /// <summary>
        ///  Finds full path to directory (e.g. directoryName: "Levels" or "Assets")
        ///  Starts from /bin/Debug folder, then goes to parent /bin, and so on.
        ///  Casts an exception if we iterated down to the root of the tree.
        /// </summary>
        private string GetPath(string directoryName) {
            DirectoryInfo dir = new DirectoryInfo(Path.GetDirectoryName(System.Reflection.Assembly.
                GetExecutingAssembly().Location));

            while (dir.Name != directoryName) {
                if (dir.FullName == dir.Root.Name) {
                    throw new FileNotFoundException(
                        $"Error: Directory \"{directoryName}\" does not exist.");
                } else {
                    foreach (var i in dir.GetDirectories()) {
                        if (i.Name == directoryName) {
                            return i.FullName;
                        }
                    }

                    dir = dir.Parent;
                }
            }
            return dir.FullName;
        }

        /// <summary>
        /// Making sure our mapfiles are not tampered with by calculating the checksum
        /// Source: https://stackoverflow.com/questions/10520048/calculate-md5-checksum-for-a-file
        /// Source: https://en.wikipedia.org/wiki/MD5
        /// </summary>
        /// <param name="filename"></param>
        public string CheckMD5(string filename)
        {
            // "using": automatically disposes the object after use,
            //  even if exception is casted
            using (var md5 = MD5.Create())
            {
                using (var stream = File.OpenRead(filename))
                {
                    var hash = md5.ComputeHash(stream);
                    return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
                }
            }
        }

        /// <summary>
        ///  Checks for invalid files and invalid filecontent
        /// </summary>
        public bool ValidMapCheck(string filename) {

            var hash = "";
            var hash32bit = "";
            Console.WriteLine("Hash: " + CheckMD5(Path.Combine(GetPath("Levels"), filename)));

            // Checks for invalid files
            if (filename != "the-beach.txt" && filename != "short-n-sweet.txt") {
                throw new ArgumentException($"Parser cannot load: {filename}");
            }

            // Checks for invalid file content
            if (filename == "the-beach.txt") {
                hash = "81b89b1908e3b3b7fcd7526810c32f14";
                hash32bit = "34d0e9c5ea54bfc60a0365f28b7d3a19";
            }
            if (filename == "short-n-sweet.txt") {
                hash = "e97f28bfff174f9643c088814377ada6";
                hash32bit = "5c4832a9a5510bdab5976ad0e6905e85";

            }

            var checksum = CheckMD5(Path.Combine(GetPath("Levels"), filename));
            if (checksum == hash || checksum == hash32bit) {
                return true;
            }
            throw new ArgumentException($"You've tampered with {filename}!");
            // Todo: Find out how to hide these hash strings and to avoid inline hardcode
        }

        /// <summary>
        ///  Collects a string[] containing all strings in the file
        /// </summary>
        private string[] ReadFile(string directoryName, string fileName) {
            var dir = GetPath(directoryName);
            string path = Path.Combine(dir, fileName);
            ValidatePath(path);
            return File.ReadAllLines(path);
        }

        /// <summary>
        ///  Extracts ASCII characters from the txt file and appends it
        ///  to an array. Later we will use this array to draw pictures.
        /// </summary>
        private List<char> GetAllTiles(string[] txtFile) {
            var charList = new List<char>();
            for (int i = 0; i < 23; i++) {
                foreach (var j in txtFile[i]) {
                    charList.Add(j);
                }
            }

            return charList;
        }

        /// <summary>
        ///    Adds all objects to the appropriate map being loaded
        ///    We iterate from top-left (0,1) of the screen to bottom-right (1,0)
        /// </summary>
        private void AddEntities(List<char> map) {
            float tmpX = Constants.X_MIN;
            float tmpY = Constants.Y_MAX;

            int index = 0;

            // Going from top (y 1.0) to bottom (y 0.0)
            while (tmpY > Constants.Y_MIN) {

                // Going from left (x 0.0) to right (x 1.0)
                while (tmpX < Constants.X_MAX) {

                    // There can be empty strings in our list of strings.
                    if (map[index].ToString() == " ") {
                        index += 1;

                    } else {
                        // Adds obstacles
                        if (Obstacles.ContainsKey(map[index])) {
                            var shape = new StationaryShape(new Vec2F(tmpX, tmpY),
                                new Vec2F(Constants.WIDTH, Constants.HEIGHT));
                            var file = Path.Combine(GetPath("Assets"),
                                "Images", Obstacles[map[index]]);
                            ValidatePath(file);
                            ObstacleEntities.Add(new Entity(shape, new Image(file)));
                        }

                        // Adds platforms
                        if (Platforms.ContainsKey(map[index])) {
                            var shape = new StationaryShape(new Vec2F(tmpX, tmpY),
                                new Vec2F(Constants.WIDTH, Constants.HEIGHT));
                            var file = Path.Combine(GetPath("Assets"), "Images",
                                Platforms[map[index]]);
                            ValidatePath(file);
                            PlatformEntities.Add(new Platform(shape, new Image(file), map[index]));
                        }

                        // Adds exits
                        if (Exits.ContainsKey(map[index])) {
                            var shape = new StationaryShape(new Vec2F(tmpX, tmpY),
                                new Vec2F(Constants.WIDTH, Constants.HEIGHT));
                            var file = Path.Combine(GetPath("Assets"), "Images",
                                Exits[map[index]]);
                            ValidatePath(file);
                            ExitEntities.Add(new Entity(shape, new Image(file)));
                        }

                        // Update index
                        index += 1;
                    }

                    tmpX += Constants.WIDTH;
                }

                tmpX = 0;
                tmpY -= Constants.HEIGHT;
            }
        }
    }
}

ParseCustomer.cs:

public partial class LevelParser {
    public Dictionary<string, Tuple<int, char, string, int, int, Entity>>
        GetCustomerInfo(string[] txtFile) {

        // Creates a dictionary containing all info about the customer
        var retDict = new Dictionary<string, Tuple<int, char, string, int, int, Entity>>();

        // Iterates over the entire txtFile and finds certain string
        IEnumerable<string> findName = txtFile.Where(l => l.StartsWith("Customer: "));
        foreach (var line in findName)
        {
            // Parses file
            var name = line.Split(' ')[1];
            var timeBeforeSpawn = Convert.ToInt32(line.Split(' ')[2]); // seconds
            var spawnOnPlatform = Convert.ToChar(line.Split(' ')[3]);
            var destinationPlat = line.Split(' ')[4]; // string
            var patienceTime = Convert.ToInt32(line.Split(' ')[5]); // seconds
            var rewardPoints = Convert.ToInt32(line.Split(' ')[6]);

            // Adds an entity
            var shape = new DynamicShape(new Vec2F(), new Vec2F());
            var image = new Image(Path.Combine("Assets", "Images", "CustomerStandLeft.png"));
            var entity = new Entity(shape, image);

            retDict.Add(name, new Tuple<int, char, string, int, int, Entity>(timeBeforeSpawn,
                spawnOnPlatform, destinationPlat, patienceTime, rewardPoints, entity));

        }

        // Now we have everything we need in one dictionary
        return retDict;
    }
}

Есть еще 3 файла, каждый из которых содержит один и тот же частичный класс, но имеет метод, который делает что-то отличное от других.

Ответы [ 4 ]

3 голосов
/ 07 июня 2019

A Частичные классы Роль в этом мире для генераторов кода и Дизайнеров .Также как разработчик вы можете воспользоваться преимуществами расширения классов, не связываясь с сгенерированным кодом .

Однако их использование за пределами этого ограничено и довольно подозрительно.

Что касается шаблонов проектирования , шаблон проектирования в целом представляет собой повторно используемое решение часто встречающейся проблемы в данном контексте при разработке программного обеспечения.Являются ли частичные классы шаблонами проектирования?хммм, ну не совсем так, как методы расширения в C # - нет.

Все, что они есть - это немного синтаксического сахара , который все равно скомпилирован в один класс.

Например

public partial class Test
{
    public int testing1 {get;set;}
}

public partial class Test
{    
    public int testing2 {get;set;}
}

Получается как это

public class Test
{
    [CompilerGenerated]
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private int <testing1>k__BackingField;

    [CompilerGenerated]
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private int <testing2>k__BackingField;

    public int testing1
    {
        [CompilerGenerated]
        get
        {
            return <testing1>k__BackingField;
        }
        [CompilerGenerated]
        set
        {
            <testing1>k__BackingField = value;
        }
    }

    public int testing2
    {
        [CompilerGenerated]
        get
        {
            return <testing2>k__BackingField;
        }
        [CompilerGenerated]
        set
        {
            <testing2>k__BackingField = value;
        }
    }
}
1 голос
/ 07 июня 2019

Не могли бы вы сказать, что разделение класса на частичные классы - это способ инкапсулировать ваш код?

Инкапсуляция относится к механизму связывания данных с методами(или другие функции), работающие с этими данными и тем самым ограничивающие прямой доступ к некоторым компонентам объекта.

, тогда как

Частичные классы предоставляют особую возможность реализовать функциональность одного класса внесколько файлов и все эти файлы объединяются в один файл класса при компиляции приложения.

Поэтому частичные классы заботятся о физическом распределении кода по файлам, а инкапсуляция заботится о логической группировке и классификации данных.и поведения, предназначенные для работы с этими данными.

почему разделение кода на частичные классы не является частью шаблона проектирования?

Шаблоны проектирования нацелены на обеспечение многоразового решенияк общей повторяющейся проблеме проектирования программного обеспечения.Эти шаблоны работают на логическом уровне, чтобы обеспечить решения для создания объекта, определения его структуры или установления его поведения с другими объектами, которые соответствуют принципам SOLID .

Когда мы находимсяРазбивая класс на несколько файлов, мы не делаем никаких логических изменений в классе, которые влияют на его связь с любым другим классом.Следовательно, частичные классы не рассматриваются как шаблоны проектирования.

1 голос
/ 07 июня 2019

partial class - это просто способ разбить определение класса на несколько файлов.

Он не является частью шаблона проектирования, поскольку он фактически не влияет на функциональность class по сравнению с «обычным» class, определенным в одном файле.

0 голосов
/ 07 июня 2019

Частичный класс по-прежнему один класс , но распределен по нескольким файлам. Пользователь частичного класса не осознает тот факт, что этот класс является частичным. С другой стороны, инкапсуляция привнесет больше классов. Инкапсуляция - это структурный принцип проектирования на уровне проектирования, а частичные классы - концепция компилятора или компоновщика на уровне компилятора.

Шаблон должен оказывать влияние на дизайн структуры, поведения или архитектуры кода, чтобы считаться шаблоном проектирования. Обычно шаблоны проектирования подразделяются на «Шаблоны проектирования», «Структурный», «Архитектурный» и «Поведенческий». Концепция частичных классов не попадает ни в одну из этих категорий. Частичное понятие класса зависит от компилятора или компоновщика, чтобы знать это понятие.

Что делает узор узором? Шаблон - это шаблон того, как сделать что-то или как достичь одной и той же цели (решить одну и ту же проблему) в разных сценариях. Шаблон не зависит от платформы компилятора, поскольку это общее решение, не зависящее от языка.

Частичные классы, представленные на языке C #, чтобы обеспечить способ отделить код разметки пользовательского интерфейса (XAML) от соответствующего кода (C #). Затем компилятор объединит два файла. На первом этапе код XAML преобразуется в C # интерпретатором XAML. Затем два частичных класса объединяются перед преобразованием в IL.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...