Как лучше организовать структуру данного кода?
Если действия симметричны, т.е. при X > (C > B > A) действия те же, что при X > (A > B > C),
можно привести переменные к однозначно возрастающему варианту — поменять местами A и C если A > C.
Так вариантов останется 7.
if (A > C) [A, C] = [C, A]; // поменять местами значения A и C
                            // теперь точно A < B < C
if (X < A) {          // вар. 0
} else if (X == A) {  // вар. 1 
} else if (X < B) {   // вар. 2
} else if (X == B) {  // вар. 3
} else if (X < C) {   // вар. 4
} else if (X == C) {  // вар. 5
} else { // X > C     // вар. 6
}

Ещё один способ — с бинарным деревом, который интуитивно но неправильно пытался подсказать xmoonlight 
Всего есть 2 * 7 = 14 вариантов. Гипотеза о том, что действия зеркальны для двух вариантов A > B > C и A < B < C вроде бы подтвердилась, поэтому оставлю первый шаг, где меняем местами A и C, чтобы последовательность точно была возрастающей.

Остаётся 7 вариантов. Достаточно 3 битов, чтобы закодировать любой из них. Каждый бит это ответ на 1 вопрос, проверка одного условия. Т.е. достаточно всего трёх if'ов для определения нужного блока кода:
0  1  2  3  4  5  6 
---o-----o-----o---   
   A     B     C      

0  1  0  1  0  1  0  1
0  0  1  1  0  0  1  1
0  0  0  0  1  1  1  1

Примерно так:if (A > C) [A, C] = [C, A]; // поменять местами значения A и C
                            // теперь точно A < B < C
if (X > B) {
  if (X > C) {    // вар. 6
  } else {
    if (X == C) { // вар. 5
    } else {      // вар. 4
    }
  }
} else {
  if (X > A) {
    if (X == B) { // вар. 3
    } else {      // вар. 2
    }
  } else {
    if (X == A) { // вар. 1
    } else {      // вар. 0
    }
  }
}Так менее наглядно и читаемо, но сэкономили на спичках – меньше проверок.
Не более 3 проверок условий, чтобы определиться, куда попали.
Все ваши случаи можно представить себе на координатной прямой и сопоставить каждый из них с числом на этой прямой. Например, если A < B < C, то числа будут положительными, а если A > B > C, то отрицательными (A ближе к нулевой отметке, а C — дальше; чем X дальше от нуля, тем его абсолютное значение больше).

A > B > C > X = -7
A > B > X == C = -6
A > B > X > C = -5
A > X == B > C = -4
A > X > B > C = -3
X == A > B > C = -2
X > A > B > C = -1
0
X < A < B < C = 1
X == A < B < C = 2
A < X < B < C = 3
A < X == B < C = 4
A < B < X < C = 5
A < B < X == C = 6
A < B < C < X = 7

Можно написать функцию, которая определит числовой индекс для каждого из этих случаев. Потом передаёте X, A, B и C в эту функцию, получаете числовой индекс конкретного случая и организуете свой код с помощью switch (case_id). Тогда в нём не будет множества условий сравнения 4 чисел, а только номера случаев, соответствующих разным числам на воображаемой координатной прямой.

Возможно, такая математическая абстракция вам будет нагляднее.

Ещё вариант: возвращать в такой вспомогательной функции не число, а строковый идентификатор вида "A < X < B < C" или "A > X == B > C". Т. е. возвращаемая функцией строка будет выглядеть так, как бы вы написали условие в понятном для себя виде. Свой рабочий код можно затем точно так же организовать средствами switch, только вместо числовых идентификаторов (которые я описал в начале) нужно использовать строковые (с понятными обозначениями условий) — по ним будет легко понять, какой случай в данный момент обрабатывается вашим кодом.

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