I have found some other ways (than Block[...]) to "force" the matching, but with some slight modifications of the code.
1. Whatever the choice you make here :
{f1, f2} = {Hold, Hold};
or
{f1, f2} = {HoldComplete, HoldComplete};
or
{f1, f2} = {Unevaluated, HoldPattern};
the two following approaches work (and give the same results) :
-> Using /; : (this is actually the more compact solution)
MatchQ[f1[Times[3, 2, 2]], f2[Times[p : __ /; Length@{p} == 1]]] MatchQ[f1[Times[3, 2, 2]], f2[Times[p : __ /; Length@{p} == 3]]] (*False*) (*True*)
-> Using a "trick" with /.
MatchQ @@ ({f1[Times[3, 2, 2]], f2[Times[a_]]} /. Times -> foo) MatchQ @@ ({f1[Times[3, 2, 2]], f2[Times[_, _, _]]} /. Times -> foo) (*False*) (*True*)
Of course, instead of Times[_,_,_] you could use conditional /; or even pattern test ?, to specify how many arguments Times should have (like in the previous solution) but this would produce a longer and less readable code.
2. All the previous code works not only when the arguments are numbers (Times[3,2,2]) but also when they are just symbolic variables (Times[a,b,c])
For symbolic variables only, this also works :
Remove[a, b, c]; (**) MatchQ[Times[a, b, c], _Times?(Length@# == 1 &)] MatchQ[Times[a, b, c], _Times?(Length@# == 3 &)] (*False*) (*True*)
In comparison, neither this works :
MatchQ[Times[a, b, c], Times[x_, y_, z_]] MatchQ[Times[a, b, c], Times[x_, y_]] MatchQ[Times[a, b, c], Times[x_]] (*True*) (*True*) (*True*)
nor the original test :
MatchQ[HoldComplete[Times[a, b, c]], HoldComplete[Times[x_, y_, z_]]] MatchQ[HoldComplete[Times[a, b, c]], HoldComplete[Times[x_]]] (*True*) (*True*)
Flat. $\endgroup$Flatattribute ofTimes. $\endgroup$