Как я должен структурировать сложные проекты в C? [закрытый]

Чтобы проиллюстрировать ответ Шлема с помощью питонического решения:

class Game:

    VALUES = list(range(1, 10))
    COLORS = ["Red", "Blue", "Yellow", "Green"]

    @classmethod
    def create_draw_stack(cls):
        return  [Card(value, color) for value in cls.VALUES for color in cls.COLORS]       

    def __init__(self, players_count=2, cards_count=7):
        self.draw_stack = self.create_draw_stack()
        self.play_stack = [self.draw_stack.pop()]

Также вы спросили:

Существует ли эмпирическое правило для интуитивного понимания таких вопросов?

«Интуитивно» часть - это, главным образом, вопрос опыта («опыт» - это не то же самое, что «время, потраченное на что-то» - это также подразумевает чтение, эксперименты, мышление и т. Д.). [ 1111]

wrt / «правило большого пальца»:

  • высокая сплоченность: одна «единица» - будь то модуль, класс, метод или функция - должна состоять из одного и только из одного (иногда эта «одна вещь» может быть довольно широкой, но в любом случае)
  • локальность: вещи, которые работают вместе, должны храниться вместе (поэтому вам не нужно открывать дюжину файлов в вашем редакторе, чтобы узнать, как все это работает работа)
  • слабая связь: один «блок» должен напрямую зависеть от как можно меньшего числа других блоков
  • читаемость: код должен быть как можно более очевидным и читаемым (хорошо, некоторые субъективные части здесь )
  • тестируемость: код должен легко тестироваться изолированно (ср. «Слабая связь»)
  • простота: код должен быть как можно более простым в зависимости от сложности задачи.
  • удобство использования: ваш «модульный» API должен быть максимально простым в использовании для клиентского кода
78
задан Stephen 23 August 2011 в 02:10
поделиться

8 ответов

Ключ является модульным принципом. Это легче разработать, реализовать, скомпилировать и поддержать.

  • Определяют модули в Вашем приложении, как классы в приложении OO.
  • интерфейс Separate и реализация для каждого модуля, вставленного в интерфейс только, что необходимо другим модулям. Помните, что нет никакого пространства имен в C, таким образом, необходимо сделать все в интерфейсах уникальным (например, с префиксом).
  • Скрывают глобальные переменные в реализации и используют функции средства доступа для чтения-записи.
  • не думают с точки зрения наследования, но с точки зрения состава. Как правило не пытайтесь подражать C++ в C, это было бы очень трудно считать и поддержать.

, Если у Вас есть время для изучения, смотрите на то, как приложение Ada структурировано с ее обязательным package (интерфейс модуля) и package body (реализация модуля).

Это для кодирования.

Для поддержания (помнят, что Вы кодируете однажды, но Вы несколько раз поддерживаете) я предлагаю зарегистрировать Ваш код; Doxygen является хорошим выбором для меня. Я предлагаю также создать сильный комплект регрессионного теста, который позволяет Вам осуществлять рефакторинг.

49
ответ дан mouviciel 24 November 2019 в 10:38
поделиться

Это - распространенное заблуждение, что методы OO не могут быть применены в C. Большинство может - это быть просто, что они являются немного более громоздкими, чем на языках с синтаксисом, выделенным заданию.

Одна из основ устойчивого проектирования системы является инкапсуляцией реализации позади интерфейса. FILE* и функции, которые работают с ним (fopen(), fread() и т.д.) хороший пример того, как инкапсуляция может быть применена в C для установления интерфейсов. (Конечно, так как C испытывает недостаток в спецификаторах доступа, Вы не можете осуществить это, никто не посмотрел в struct FILE, но только мазохист сделал бы так.)

При необходимости, полиморфное поведение может иметься в C использование таблиц указателей функции. Да, синтаксис ужасен, но эффект совпадает с виртуальными функциями:

struct IAnimal {
    int (*eat)(int food);
    int (*sleep)(int secs);
};

/* "Subclass"/"implement" IAnimal, relying on C's guaranteed equivalence
 * of memory layouts */
struct Cat {
    struct IAnimal _base;
    int (*meow)(void);
};

int cat_eat(int food) { ... }
int cat_sleep(int secs) { ... }
int cat_meow(void) { ... }

/* "Constructor" */
struct Cat* CreateACat(void) {
    struct Cat* x = (Cat*) malloc(sizeof (struct Cat));
    x->_base.eat = cat_eat;
    x->_base.sleep = cat_sleep;
    x->meow = cat_meow;
}

struct IAnimal* pa = CreateACat();
pa->eat(42);                       /* Calls cat_eat() */

((struct Cat*) pa)->meow();        /* "Downcast" */
30
ответ дан j_random_hacker 24 November 2019 в 10:38
поделиться

Все хорошие ответы.

я только добавил бы, "минимизируют структуру данных". Это могло бы даже быть легче в C, потому что, если C++ "C с классами", ООП пытается поощрить Вас брать каждое существительное / глагол в Вашей голове и превращать его в класс / метод. Это может быть очень расточительно.

, Например, предположите, что у Вас есть массив температурных чтений в моментах времени, и Вы хотите отобразить их как линейную диаграмму в Windows. Windows имеет сообщение КРАСКИ, и когда Вы получаете его, можно циклично выполнить посредством выполнения массива функции LineTo, масштабируя данные, когда Вы идете для преобразования его в пиксельные координаты.

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

, Таким образом, Вы заканчиваете с огромным объемом кода, который является oh-so-readable и просто тратит 90% из, время, управляя объектами.

Все это сделано от имени "хорошей практики программирования" и "эффективности".

, По крайней мере, в C простой, эффективный путь будет более очевидным, и искушение создать менее сильные пирамиды.

12
ответ дан Mike Dunlavey 24 November 2019 в 10:38
поделиться

стандарты кодирования GNU развились за несколько десятилетий. Это была бы хорошая идея считать их, даже если Вы не следуете за ними к букве. Размышление о вопросах, поднятых в них, дает Вам более устойчивое основание о том, как структурировать Ваш собственный код.

11
ответ дан 24 November 2019 в 10:38
поделиться

Я был бы suggets Вы для проверки кода любого популярного проекта открытого исходного кода C, как... хм... Ядро Linux или Мерзавец; и посмотрите, как они организуют его.

3
ответ дан Ivan Krechetov 24 November 2019 в 10:38
поделиться

Правило числа для сложного приложения: должно быть легко читать.

Для подавания сложной заявки simplier я использую , Делят и завоевывают .

3
ответ дан MrValdez 24 November 2019 в 10:38
поделиться

Если Вы знаете, как структурировать Ваш код в Java или C++, то можно следовать за теми же принципами с кодом C. Единственная разница - то, что у Вас нет компилятора рядом, и необходимо сделать все дополнительное тщательно вручную.

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

у Вас не может быть классов с C, но можно легко реализовать "Абстрактные типы данных". Вы создаете.C и.H файл для каждого абстрактного типа данных. Если Вы предпочитаете, чтобы у Вас могло быть два заголовочных файла, одна общественность и один частный. Идея состоит в том, что все структуры, константы и функции, которые должны быть экспортированы, переходят к общедоступному заголовочному файлу.

Ваши инструменты также очень важны. Полезный инструмент для C линт , который может помочь Вам найти неприятные запахи в своем коде. Другим инструментом, который можно использовать, является Doxygen, который может помочь Вам генерировать документация .

3
ответ дан kgiannakakis 24 November 2019 в 10:38
поделиться

Инкапсуляция является всегда ключевой для успешной разработки, независимо от языка разработки.

А обманывают, я раньше помогал инкапсулировать "частные" методы в C, не должен включать их прототипы в ".h" файл.

3
ответ дан Nate 24 November 2019 в 10:38
поделиться
Другие вопросы по тегам:

Похожие вопросы: